changeset 9391:ea88fce5f7ff octave-forge

queueing: add new package for Moreno Marzolla
author paramaniac
date Fri, 03 Feb 2012 12:07:15 +0000
parents e090b67004a1
children 71320d3ebfee
files main/queueing/COPYING main/queueing/DESCRIPTION main/queueing/DESCRIPTION.in main/queueing/INSTALL main/queueing/Makefile main/queueing/README main/queueing/broken/Makefile main/queueing/broken/blkdiagonalize.m main/queueing/broken/dtmc_period.m main/queueing/broken/lee_et_al_98.m main/queueing/broken/qnmmmk_alt.m main/queueing/broken/qnopenmultig.m main/queueing/broken/qnopensinglenexp.m main/queueing/doc/Makefile main/queueing/doc/ack.txi main/queueing/doc/contributing.txi main/queueing/doc/gettingstarted.txi main/queueing/doc/gpl.txi main/queueing/doc/installation.txi main/queueing/doc/markovchains.txi main/queueing/doc/queueing.html main/queueing/doc/queueing.pdf main/queueing/doc/queueing.texi main/queueing/doc/queueingnetworks.txi main/queueing/doc/singlestation.txi main/queueing/doc/summary.txi main/queueing/examples/Makefile main/queueing/examples/demo_1_ctmc.m main/queueing/examples/demo_1_ctmc_exps.m main/queueing/examples/demo_1_ctmc_fpt.m main/queueing/examples/demo_1_ctmc_mtta.m main/queueing/examples/demo_1_ctmc_taexps.m main/queueing/examples/demo_1_dtmc.m main/queueing/examples/demo_1_dtmc_fpt.m main/queueing/examples/demo_1_qnclosed.m main/queueing/examples/demo_1_qnclosedmultimva.m main/queueing/examples/demo_1_qnclosedmultimvaapprox.m main/queueing/examples/demo_1_qnclosedsinglemva.m main/queueing/examples/demo_1_qnclosedsinglemvaapprox.m main/queueing/examples/demo_1_qnconvolution.m main/queueing/examples/demo_1_qnmmm.m main/queueing/examples/demo_1_qnopensingle.m main/queueing/examples/demo_1_qnsolve.m main/queueing/examples/demo_1_qnvisits.m main/queueing/examples/demo_2_ctmc.m main/queueing/examples/demo_2_ctmc_mtta.m main/queueing/examples/demo_2_ctmc_taexps.m main/queueing/examples/demo_3_ctmc.m main/queueing/examples/grabdemo.m main/queueing/inst/Makefile main/queueing/inst/ctmc.m main/queueing/inst/ctmc_bd.m main/queueing/inst/ctmc_bd_solve.m main/queueing/inst/ctmc_exps.m main/queueing/inst/ctmc_fpt.m main/queueing/inst/ctmc_mtta.m main/queueing/inst/ctmc_solve.m main/queueing/inst/ctmc_taexps.m main/queueing/inst/dtmc.m main/queueing/inst/dtmc_check_P.m main/queueing/inst/dtmc_fpt.m main/queueing/inst/dtmc_solve.m main/queueing/inst/population_mix.m main/queueing/inst/qnammm.m main/queueing/inst/qnclosed.m main/queueing/inst/qnclosedab.m main/queueing/inst/qnclosedbsb.m main/queueing/inst/qnclosedgb.m main/queueing/inst/qnclosedmultimva.m main/queueing/inst/qnclosedmultimvaapprox.m main/queueing/inst/qnclosedpb.m main/queueing/inst/qnclosedsinglemva.m main/queueing/inst/qnclosedsinglemvaapprox.m main/queueing/inst/qnclosedsinglemvald.m main/queueing/inst/qncmva.m main/queueing/inst/qnconvolution.m main/queueing/inst/qnconvolutionld.m main/queueing/inst/qnjackson.m main/queueing/inst/qnmarkov.m main/queueing/inst/qnmg1.m main/queueing/inst/qnmh1.m main/queueing/inst/qnmix.m main/queueing/inst/qnmknode.m main/queueing/inst/qnmm1.m main/queueing/inst/qnmm1k.m main/queueing/inst/qnmminf.m main/queueing/inst/qnmmm.m main/queueing/inst/qnmmmk.m main/queueing/inst/qnmvablo.m main/queueing/inst/qnmvapop.m main/queueing/inst/qnopen.m main/queueing/inst/qnopenab.m main/queueing/inst/qnopenbsb.m main/queueing/inst/qnopenmulti.m main/queueing/inst/qnopensingle.m main/queueing/inst/qnsolve.m main/queueing/inst/qnvisits.m main/queueing/scripts/Makefile main/queueing/scripts/gethelp.cc main/queueing/scripts/mkdoc main/queueing/scripts/munge-texi.cc main/queueing/scripts/pmva.l main/queueing/scripts/pmva.y main/queueing/scripts/test.pmva main/queueing/test/Makefile main/queueing/test/fntests.m
diffstat 106 files changed, 22131 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/COPYING	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 3 of the License, or
+    (at your option) any later version.
+
+    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.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/DESCRIPTION	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,16 @@
+Name: queueing
+Version: 1.0.0
+Date: 2012-02-03
+Author: Moreno Marzolla <marzolla@cs.unibo.it>
+Maintainer: Moreno Marzolla <marzolla@cs.unibo.it>
+Title: Queueing Networks and Markov chains analysis package for GNU Octave
+Description: This package provides functions
+ for analyzing single station queueing systems,
+ Queueing Network and Markov chains.  Open, closed and mixed
+ networks with single or multiple job classes are supported;
+ exact and approximate solution techniques are available.
+Categories: Misc
+Depends: octave (>= 3.0.0)
+Autoload: yes
+License: GPL version 3 or later
+Url: http://www.moreno.marzolla.name/software/queueing/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/DESCRIPTION.in	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,16 @@
+Name: PROGNAME
+Version: VERSIONNUM
+Date: VERSIONDATE
+Author: Moreno Marzolla <marzolla@cs.unibo.it>
+Maintainer: Moreno Marzolla <marzolla@cs.unibo.it>
+Title: Queueing Networks and Markov chains analysis package for GNU Octave
+Description: This package provides functions
+ for analyzing single station queueing systems,
+ Queueing Network and Markov chains.  Open, closed and mixed
+ networks with single or multiple job classes are supported;
+ exact and approximate solution techniques are available.
+Categories: Misc
+Depends: octave (>= 3.0.0)
+Autoload: yes
+License: GPL version 3 or later
+Url: http://www.moreno.marzolla.name/software/queueing/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/INSTALL	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,167 @@
+This file documents the installation procedure of the `queueing'
+toolbox.
+
+   `queueing' is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License, version 3 or
+later, as published by the Free Software Foundation.
+
+     Note: This file (`INSTALL') is automatically generated from
+     `doc/installation.txi' in the `queueing' sources.  Do not modify
+     this document directly, as changes will be lost. Modify the source
+     `doc/installation.txi' instead.
+
+1 Installing the queueing toolbox
+*********************************
+
+1.1 Installation through Octave package management system
+=========================================================
+
+The most recent version of `queueing' is 1.0.0 and can be downloaded
+from
+
+`http://www.moreno.marzolla.name/software/queueing/queueing-1.0.0.tar.gz'
+
+   To install `queueing' in the system-wide location, such that all
+functions are automatically available when Octave starts, you can use
+`pkg install' command. At the Octave prompt, type the following:
+
+     octave:1> pkg install queueing-1.0.0.tar.gz
+
+   (Note: you may need to start Octave as root in order to allow the
+installation to copy the files to the target locations). After this,
+all functions will be readily available each time Octave starts,
+without the need to tweak the search path. To uninstall `queueing', use
+the `pkg uninstall queueing' command.
+
+   If you do not have root access, you can do a local installation by
+issuing the following command at the Octave prompt:
+
+     octave:1> pkg install -local queueing-1.0.0.tar.gz
+
+   This will install `queueing' within the user's home directory, and
+the package will be available to that user only. *Note:* Octave version
+3.2.3 as shipped with Ubuntu 10.04 seems to ignore `-local' and always
+tries to install the package on the system directory.
+
+1.2 Manual installation
+=======================
+
+If you want to install `queueing' in a custom location, you can
+download the source tarball from the URL above, and unpack it somewhere:
+
+     tar xfz queueing-1.0.0.tar.gz
+     cd queueing-1.0.0/
+
+   Copy all `.m' files from the `inst/' directory to some target
+location. Then, you can start Octave with the `-p' option to add the
+target location to the search path, so that Octave will find all
+`queueing' functions automatically:
+
+     octave -p _/path/to/queueing_
+
+   For example, if all `queueing' m-files are in `/usr/local/queueing',
+you can start Octave as follows:
+
+     octave -p `/usr/local/queueing'
+
+   If you want, you can add the following line to `~/.octaverc':
+
+     addpath("_/path/to/queueing_");
+
+so that the path `/usr/local/queueing' is automatically added to the
+search path each time Octave is started, and you no longer need to
+specify the `-p' option on the command line.
+
+1.3 Content of the source distribution
+======================================
+
+The `queueing' source distribution contains the following
+subdirectories:
+
+`doc/'
+     Documentation source. Most of the documentation is extracted from
+     the comment blocks of individual function files from the `inst/'
+     directory.
+
+`inst/'
+     This directory contains the m-files which implement the various
+     Queueing Network algorithms provided by `queueing'. As a
+     notational convention, the names of source files containing
+     functions for Queueing Networks start with the `qn' prefix; the
+     name of source files containing functions for Continuous-Time
+     Markov Chains (CTMSs) start with the `ctmc' prefix, and the names
+     of files containing functions for Discrete-Time Markov Chains
+     (DTMCs) start with the `dtmc' prefix.
+
+`test/'
+     This directory contains the test functions used to invoke all
+     tests on all function files.
+
+`scripts/'
+     This directory contains some utility scripts mostly from GNU
+     Octave, which extract the documentation from the
+     specially-formatted comments in the m-files.
+
+`examples/'
+     This directory contains examples which are automatically extracted
+     from the `demo' blocks of the function files.
+
+`broken/'
+     This directory contains function files which are either not working
+     properly, or need additional testing before they can be moved to
+     the `inst/' directory.
+
+
+   The `queueing' package ships with a Makefile which can be used to
+produce the documentation (in PDF and HTML format), and automatically
+execute all function tests. Specifically, the following targets are
+defined:
+
+`all'
+     Running `make' (or `make all') on the top-level directory builds
+     the programs used to extract the documentation from the comments
+     embedded in the m-files, and then produce the documentation in PDF
+     and HTML format (`doc/queueing.pdf' and `doc/queueing.html',
+     respectively).
+
+`check'
+     Running `make check' will execute all tests contained in the
+     m-files. If you modify the code of any function in the `inst/'
+     directory, you should run the tests to ensure that no errors have
+     been introduced. You are also encouraged to contribute new tests,
+     especially for functions which are not adequately validated.
+
+`clean'
+`distclean'
+`dist'
+     The `make clean', `make distclean' and `make dist' commands are
+     used to clean up the source directory and prepare the distribution
+     archive in compressed tar format.
+
+
+1.4 Using the queueing toolbox
+==============================
+
+You can use all functions by simply invoking their name with the
+appropriate parameters; the `queueing' package should display an error
+message in case of missing/wrong parameters. You can display the help
+text for any function using the `help' command. For example:
+
+     octave:2> help qnmvablo
+
+   prints the documentation for the `qnmvablo' function.  Additional
+information can be found in the `queueing' manual, which is available
+in PDF format in `doc/queueing.pdf' and in HTML format in
+`doc/queueing.html'.
+
+   Within GNU Octave, you can also run the test and demo blocks
+associated to the functions, using the `test' and `demo' commands
+respectively. To run all the tests of, say, the `qnmvablo' function:
+
+     octave:3> test qnmvablo
+     -| PASSES 4 out of 4 tests
+
+   To execute the demos of the `qnclosed' function, use the following:
+
+     octave:4> demo qnclosed
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/Makefile	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,52 @@
+VERSIONNUM=1.0.0
+VERSIONDATE="2012-02-03"
+PROGNAME=queueing
+
+DISTNAME=$(PROGNAME)-$(VERSIONNUM)
+SUBDIRS=inst scripts examples doc test broken
+DISTFILES=COPYING README Makefile DESCRIPTION DESCRIPTION.in INSTALL
+
+.PHONY: clean check
+
+ALL: DESCRIPTION doc/conf.texi
+	for d in $(SUBDIRS); do \
+		$(MAKE) -C $$d ALL; \
+	done
+
+doc/conf.texi:
+	\rm -f doc/conf.texi
+	echo "@set VERSION $(VERSIONNUM)" > doc/conf.texi
+	echo "@set VERSIONDATE $(VERSIONDATE)" >> doc/conf.texi
+	echo "@set top_srcdir " `pwd` >> doc/conf.texi
+
+DESCRIPTION: DESCRIPTION.in
+	cat DESCRIPTION.in | \
+	sed "s/PROGNAME/$(PROGNAME)/g" | \
+	sed "s/VERSIONNUM/$(VERSIONNUM)/g" | \
+	sed "s/VERSIONDATE/$(VERSIONDATE)/g" > DESCRIPTION
+
+check:
+	$(MAKE) -C test check
+
+clean:
+	for d in $(SUBDIRS); do \
+		$(MAKE) -C $$d clean; \
+	done
+	\rm -r -f *~ *.tar.gz $(DISTNAME)
+
+distclean: clean
+	for d in $(SUBDIRS); do \
+		$(MAKE) -C $$d distclean; \
+	done
+	\rm -r -f doc/conf.texi fname DESCRIPTION $(DISTNAME) $(DISTNAME).tar.gz
+
+dist: ALL
+	\rm -r -f $(DISTNAME) fname
+	mkdir $(DISTNAME)
+	echo "$(DISTNAME)" > fname
+	for d in $(SUBDIRS); do \
+		mkdir -p $(DISTNAME)/$$d; \
+		$(MAKE) -C $$d dist; \
+	done
+	ln $(DISTFILES) $(DISTNAME)/
+	tar cfz $(DISTNAME).tar.gz $(DISTNAME)/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/README	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,52 @@
+===============================================================================
+                        The Octave queueing toolbox
+===============================================================================
+
+Copyright (C) 2008, 2009, 2010, 2011, 2012
+Moreno Marzolla <marzolla (at) cs.unibo.it>
+
+The queueing toolbox ("queueing", in short) is a collection of GNU
+Octave scripts for numerical evaluation of queueing network
+models. Open, closed and mixed networks are supported, with single or
+multiple classes of customers. The queueing toolbox also provides
+functions for steady-state and transient analysis of Markov chains, as
+well as fo single station queueing systems.
+
+The latest version of the queueing package can be downloaded from
+
+http://www.moreno.marzolla.name/software/queueing/
+
+This package requires GNU Octave; version 3.0.0 or later should work.
+The package contains the following directories:
+
+inst/        
+	Contains the Octave m-scripts implementing all functions
+	provided by the queueing toolbox.
+
+doc/
+        Contains the user documentation, which is automatically
+        generated from the texinfo strings embedded in the m-scripts.
+
+scripts/ 
+        This directory contains some scripts used to extract
+        documentation strings from the m-files. The scripts here have
+        been taken almost verbatim from the GNU Octave distribution.
+
+examples/
+        This directory contains demo functions which are automatically
+        extracted from the scripts included in the inst/
+        directory. The demo functions are put in this directory so
+        that they can be easily embedded into the package documentation.
+
+test/
+        This directory contains the script used to execute all
+        tests embedded within functions in the inst/ directory.
+
+broken/
+	This directory contains scripts which are currently known
+	not to work correctly, or which are work-in-progress.
+
+This package can be distributed according to the terms of the GNU
+General Public License, version 3 or later. See the file COPYING for
+details.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/broken/Makefile	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,14 @@
+DISTFILES=Makefile $(wildcard *.m)
+
+.PHONY: check dist clean
+
+ALL:
+
+dist:
+	ln $(DISTFILES) ../`cat ../fname`/broken/
+
+clean:
+	\rm -f *~
+
+distclean: clean
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/broken/blkdiagonalize.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,151 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{T} @var{p}] =} blkdiagonalize (@var{M})
+##
+## @strong{WARNING: this function has not been sufficiently tested}
+##
+## Given a square matrix @var{M}, return a new matrix @code{@var{T} =
+## @var{M}(@var{p},@var{p})} such that @var{T} is in block triangular
+## form.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item M 
+##
+## Square matrix to be permuted in block-triangular form
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item T
+##
+## The matrix @var{M} permuted in block-triangular form
+##
+## @item p
+##
+## Vector representing the permutation. The matrix @var{T}
+## can be derived as @code{@var{T} = @var{M}(@var{p},@var{p})}
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [T p] = blkdiagonalize( M )
+  if ( nargin =! 1 )
+    print_usage();
+  endif
+  n = rows(M);
+  ( [n,n] == size(M) ) || \
+      error( "M must be a square matrix" );
+  p = linspace(1,n,n); # identify permutation
+  T = M;
+  for i=1:n-1 # loop over rows
+    ## find zero and nonzero elements in the i-th row
+    zel = find(T(i,:)==0);
+    nzl = find(T(i,:)!=0);
+    zeroel = zel(zel>i);
+    nzeroel = nzl(nzl>i);
+    perm = [ 1:i nzeroel zeroel ];
+    
+    ## Update permutation
+    p = p(perm);
+    ## Permute matrix
+    T = T(perm,perm);
+  endfor
+endfunction
+%!demo
+%! P = [0 0.4 0 0 0.3 0 0 0.3 0; ...
+%!      0.3 0 0 0 0 0 0 0.7 0; ...      
+%!      0 0 0 0 0 0.3 0 0 0.7; ...
+%!      0 0 0 0 1 0 0 0 0; ...
+%!      0.3 0 0 0 0 0 0.7 0 0; ...
+%!      0 0 0.3 0 0 0 0 0 0.7; ...
+%!      1.0 0 0 0 0 0 0 0 0; ...
+%!      0 0 0 1.0 0 0 0 0 0; ...
+%!      0 0 0.4 0 0 0.6 0 0 0];
+%! P = blkdiagonalize(P);
+%! spy(P);
+
+%!demo
+%! A = ones(3);
+%! B = 2*ones(2);
+%! C = 3*ones(3);
+%! D = 4*ones(7);
+%! E = 5*ones(2);
+%! M = blkdiag(A, B, C, D, E);
+%! n = rows(M);
+%! spy(M);
+%! printf("Press any key or wait 5 seconds...");
+%! pause(5);
+%! p = randperm(n);
+%! M = M(p,p);
+%! spy(M);
+%! printf("Scrambled matrix. Press any key or wait 5 seconds...");
+%! pause(5);
+%! T = blkdiagonalize(M);
+%! spy(T);
+%! printf("Unscrambled matrix" );
+
+%!xtest
+%! A = ones(3);
+%! B = 2*ones(2);
+%! C = 3*ones(3);
+%! D = 4*ones(7);
+%! E = 5*ones(2);
+%! M = blkdiag(A, B, C, D, E);
+%! n = rows(M);
+%! p = randperm(n);
+%! Mperm = M(p,p);
+%! T = blkdiagonalize(Mperm);
+%! assert( T, M );
+
+## Given a block diagonal matrix M, find the size of all blocks. b(i) is
+## the size of i-th along the diagonal. A zero matrix is considered to
+## have a single block of size equal to the size of the matrix.
+function b = findblocks(M)
+  n = rows(M);
+  (size(M) == [n,n]) || \
+      error("M must be a square matrix");
+  b = [];
+  i=d=1;
+  while( d<n )
+    bb = M(i:d,i:d);
+    z1 = M(i:d,d+1:n);
+    z2 = M(d+1:n,i:d);
+    if ( any(bb) && !any(z1) && !any(z2) )
+      b = [b d-i+1];
+      i=d+1;
+      d=i;
+    else
+      d++;
+    endif
+  endwhile
+  if (i<d)
+    b = [b d-i+1];
+  endif
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/broken/dtmc_period.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,72 @@
+## Copyright (C) 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{p} =} dtmc_period (@var{P})
+##
+## @cindex Markov chain, discrete-time
+##
+## Compute the period @code{@var{p}(i)} of state @math{i}, for all
+## states. The period is defined as the greatest common divisor
+## of the number of steps after which a DTMC returns to the starting 
+## state. If state @math{i} is non recurrent, then @code{@var{p}(i) = 0}.
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function p = dtmc_period( P )
+
+  n = dtmc_check_P(P); # ensure P is a transition probability matrix
+
+  period = zeros(n,n); #  period(i,j) = 1 iff state i returns to itself after exactly j steps
+
+  Pi = P;
+  
+  for j=1:n
+    d = (diag(Pi) > 0); ## d(i) = 1 iff state i returns to itself after j steps
+    period(:,j) = d;
+    Pi = Pi*P;
+  endfor
+
+  p = zeros(1,n);
+  F = (find( any(period > 0, 2) )'); # F = set of recurrent states
+  for i=F
+    p(i) = gcd( find(period(i,:) > 0) );
+  endfor
+endfunction
+%!test
+%! P = [0 1 0; 0 0 1; 1 0 0]; # 1 -> 2 -> 3 -> 1
+%! p = dtmc_period(P);
+%! assert( p, [3 3 3] );
+
+%!test
+%! P = [1 0 0; 0 1 0; 0 0 1];
+%! p = dtmc_period(P);
+%! assert( p, [1 1 1] );
+
+%!test
+%! P = [0 1 0; 0 0 1; 0 1 0]; # state 1 is non recurrent
+%! p = dtmc_period(P);
+%! assert( p, [0 2 2] );
+
+%!test
+%! P = [0 0 1 0; 0 0 0 1; 0 1 0 0; 0.5 0 0.5 0];
+%! p = dtmc_period(P);
+%! assert( p, [1 1 1 1] );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/broken/lee_et_al_98.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,1249 @@
+## Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{P} =} lee_et_al_98 ( @var{mu}, @var{mu0}, @var{C}, @var{r}, @var{blocking_type} )
+##
+## @strong{WARNING: this implementation is not working yet}
+##
+## Implementation of the numerical algorithm for approximate solution of
+## single-class queueing networks with blocking. The algorithm is
+## described in [1]. This is the implementation of Algorithm 1, p. 192
+## from the paper above, and can be used for cyclic networks.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item mu
+##
+## @code{@var{mu}(i)} is the service rate at service center @math{i}.
+## This function aborts if @code{@var{mu}(i) <= 0} for some @math{i}.
+##
+## @item mu0
+##
+## @code{@var{mu0}(i)} is the external arrival rate on service center
+## @math{i}. If @code{@var{mu0}(i) <= 0} there is no external arrival on
+## service center @math{i}.
+##
+## @item C
+##
+## @code{@var{C}(i)} is the capacity of service center @math{i}. The
+## buffer size of service center @math{i} is @code{@var{C}(i)-1}. This
+## function aborts if @code{@var{C}(i) < 1} for some @math{i}.
+##
+## @item r
+##
+## @code{@var{r}(i,j)} is the routing probability from service center
+## @math{i} to service center @math{j}, that is, the probability that a
+## job which completed execution on service center @math{i} is routed to
+## service center @math{j}. If @math{\sum_{j} r(i,j) < 1} for some
+## @math{i}, then the exit probability of jobs from service center
+## @math{i} is @math{( 1 - \sum_{j} r(i,j) )}; this is the probability
+## that a job leaves the system after completing service at service
+## center @math{i}.
+##
+## @item blocking_type
+##
+## if @code{@var{blocking_type}(j)==0}, then Blocking-after-service
+## (BAS) is assumed for service center @code{@var{S_u1}(j)}; if
+## @code{@var{blocking_type}(j)!=0}, then Repetitive-service blocking is
+## assumed for service center @code{@var{S_u1}(j)}. Note that
+## Repetitive-service blocking can only be applied to saturated service
+## centers (that is, @code{@var{blocking_type}(j)} can be set != 0 only
+## if @code{@var{mu0}(j) > 0}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item P
+##
+## @code{@var{P}(i,n)} is the steady state probability that there are
+## @math{n-1} jobs on service center @math{i}.
+##
+## @end table
+##
+## @strong{ISSUES}
+##
+## This implementation makes heavy use of global variables. The reason
+## is that there are are many structures which must be passed along to
+## different functions. To avoid cluttering the function definitions
+## with lot of extra parameters, we make use of global variables.
+##
+## This implementation makes heavy use of multidimensional arrays.
+## Unfortunately, index of octave arrays always start from 1. This is an
+## issue, as in many cases (e.g., @var{P_b}), one of the indexes is
+## supposed to start from 0. Thus, pay extra attention about the range
+## of indexes.
+##
+## The algorithm by Lee et al. [1] makes heavy use of summations. While
+## in octave it is relatively easy to sum the elements of an array (or
+## of a matrix), it is not so easy to make nested summations, especially
+## if these summations cannot be reduced to matrix/vector
+## multiplications. In this implementation, there are many places in
+## which nested loops are used. This is inefficient and makes the code
+## difficult to read, but again, my understanding is that there is no
+## better way to do that.
+##
+## This implementation is NOT optimized for speed. DO NOT perform speed
+## benchmark on this implementation!
+##
+## @strong{REFERENCES}
+##
+## @noindent [1] H.S. Lee, A. Bouhchouch, Y. Dallery and Y. Frein,
+## @cite{Performance evaluation of open queueing networks with arbitrary
+## configuration and finite buffers}, Annals of Operations Research
+## 79(1998), 181-206
+##
+## @noindent [2] Hyo-Seong Lee; Stephen M. Pollock, @cite{Approximation
+## Analysis of Open Acyclic Exponential Queueing Networks with
+## Blocking}, Operations Research, Vol. 38, No. 6. (Nov. - Dec., 1990),
+## pp. 1123-1134.
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla@cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+## Created: 2007-11-20
+
+global mu;      # mu(1,j) j=1..M is the service rate at S_j
+global C;       # C(1,j) j=1..M is the capacity of buffer B_j
+global r;       # r(i,j) i=1..M, j=1..M is the routing probability from S_i to S_j
+global C_max;   # Scalar representing the maximum value of C()
+global N_max;   # Scalar representing the maximum value of N()
+global mu_u;    # mu_u(i,j) i=1..N(j), j=1..M is the upstream service rate of S_u i(j)
+global P_b;     # P_b(i,n+1,j) is the probability that at service completion instant, the server S_ui(j) sees n other servers being blocked by S_d(j), n=0..N_j-1
+global P_s;     # P_s(j) is the probability that S_d(j) is starved at the service completion instant, j=1..M
+global P;       # P(n+1,j) is the steady state probability that T(j) is in state n, n=0,..C_j+N_j
+global b_u;     # bu_u(i,n+1,j) is the probability that n servers are blocked by S_d(j), including S_ui(j), n=1..N_j
+global mu_d;    # mu_d(j) is the service rate of S_d(j), j=1..M
+global mu0;     # mu0(j) is the external arrival rate to service center S_j, j=1..M. If S_j has no external arrival, then mu0(j) = 0
+global blocking_type; # array of integer: blocking_type(j) == 0 iff the blocking type of S_u1(j) is BAS, blocking_type(j) != 0 for repetitive-service blocking
+
+## Exported constants
+global epsilon = 1e-5; # Maximum allowed error on throughput
+global iter_count_max = 100; # Maximum number of iterations
+
+function result = lee_et_al_98( mu_, mu0_, C_, r_, blocking_type_ )
+
+  error( "*** The lee_et_al_98() function is currently BROKEN. Please do not use it ***" );
+
+  global mu; 
+  global C; 
+  global r; 
+  global C_max;
+  global N_max;
+  global mu_u; 
+  global P_b;
+  global P_s;
+  global P;
+  global b_u;
+  global mu_d;
+  global mu0;
+  global blocking_type; 
+
+  if ( nargin != 5 )
+    print_usage();
+  endif
+      
+  M = size(mu_, 2); ## Number of service centers
+  size(C_) == [1,M] || error( "lee_et_al_98() - parameter C must be a (1xM) vector" );
+  size(r_) == [M,M] || error( "lee_et_al_98() - parameter r must be a (MxM) matrix" );
+  size(mu0_) == [1,M] || error( "lee_et_al_98() - parameter mu0 must be a (1,M) vector" );
+  all( C_ > 0 ) || error( "lee_et_al_98() - parameter C must contain elements >0 only" );
+  all( mu_ > 0 ) || error( "lee_et_al_98() - parameter mu must contain elements >0 only" );
+  size(blocking_type_) == [1,M] || error( "lee_et_al_98() - parameter blocking_type bust be a (1,M) vector" );
+
+  blocking_type = blocking_type_;
+  mu = [mu_ mu0_];
+  mu0 = mu0_;
+  C = C_;
+  r = r_;
+
+  ## Some constants
+  global epsilon;
+  global iter_count_max;
+
+  ## Some variables
+  C_max = max(C); # Maximum buffer size
+  N_max = M+1; # M service centers plus one (optional) external source
+  mu_d = mu_;
+  P_b = zeros( N_max, N_max, M ); ## WARNING: the second index should start from 0, not 1!!
+  P_s = zeros( M );
+  P = zeros( C_max+N_max+1, M ); ## WARNING: the first index should start from 0m not 1!!
+  b_u = zeros( M, N_max+1, M ); ## WARNING: the second index should start from 0, not 1!!
+
+  mu_u = zeros( N_max, M );
+  for j = 1:M
+    for i = 1:N(j)
+      k = f(i,j);
+      if ( is_saturated(i,j) ) 
+        mu_u(i,j) = mu(k);
+      else
+        mu_u(i,j) = mu(k) * r(k,j);
+      endif
+    endfor
+  endfor
+  ## End initialization step
+
+  ## Iteration step
+  iter_count = 1;
+
+  do
+
+    old_mu_u = mu_u;  
+    old_mu_d = mu_d;
+    
+    for j=1:M # Begin iteration step
+
+      P(1:C(j)+N(j)+1,j) = compute_P(j)';
+      assert( sum(P(:,j)), 1, 1e-4 );
+
+      ##
+      ## 1 Calculate mu_d(j) using (5)
+      ##
+      mu_d( j ) = compute_mu_d( j );
+      
+      ##
+      ## 2 Calculate P_s(j) using (3)    
+      ##
+      P_s( j ) = compute_P_s( j );
+      
+      ##
+      ## 3 Calculate P_b i(n:j) using (4) for n=0:N_j-1, i=1:N_j
+      ##
+      for i=1:N(j)
+        for n=1:N(j)
+          b_u(i,n+1,j) = compute_b_u(i,n,j);
+        endfor
+      endfor
+      for i=1:N(j)
+        for n=0:N(j)-1
+          P_b(i,n+1,j) = compute_P_b( i, n, j );
+        endfor
+        ##assert( sum(P_b(i,:,j)), 1, 1e-4 );
+      endfor
+      
+      ##
+      ## 4 Calculate mu_u(i,k) using (7) for all k \in D_j where f(i,k)=j
+      ##
+      for k=D(j)
+        i = f_inverse_i(j,k);
+        assert(f(i,k)==j);
+        mu_u(i,k) = compute_mu_u(i,k);
+      endfor    
+
+    endfor # End iteration step
+    
+    err1 = abs( old_mu_d - mu_d );
+    err2 = abs( old_mu_u - mu_u );
+    iter_count++;
+    
+  until ( ( (err1 < epsilon) && (err2 < epsilon) ) 
+         || (iter_count > iter_count_max) );
+
+  ## fprintf("Converged in %d iterations\n", iter_count );
+
+  ## Safety check: check that eq. (8) from [1] is satisfied
+  for j=1:M
+    for i=1:N(j)
+      k = f(i,j);
+      if ( k<=M )
+        assert( compute_X_u(i,j), compute_X_d(k) * r(k,j), 1e-4 );
+      endif
+    endfor
+  endfor
+  ## End safety check
+
+  ## Reshape the result, so that result(i,n) is P_i(n+1), the
+  ## steady-state probability that (n+1) customers are in service center
+  ## S_i, n=1..C(j)+1
+  result = zeros( M, C_max+1 );
+  for j=1:M
+    ## P_j = compute_P(j);
+    result(j,[1:C(j)]) = P([1:C(j)],j)';
+    result(j,C(j)+1) = sum( P([C(j)+1:C(j)+N(j)+1],j) );
+  endfor
+
+endfunction
+%!test
+%! source("lee_et_al_98.m"); # This is used to check internal functions
+
+##############################################################################
+## Usage: result = lee_et_al_98_acyclic( mu, mu0, C, r, blocking_type )
+##
+## This is Algorithm 2, p. 193 [1], and can be used for acyclic networks
+## only. The parameters have the exact same meaning as in function
+## lee_et_al_98().
+# function result = lee_et_al_98_acyclic( mu_, mu0_, C_, r_, blocking_type_ )
+
+#   global mu;    # mu(1,j) j=1..M is the service rate at S_j
+#   global C;     # C(1,j) j=1..M is the capacity of buffer B_j
+#   global r;     # r(i,j) i=1..M, j=1..M is the routing probability from S_i to S_j
+#   global C_max;
+#   global N_max;
+#   global mu_u;  # mu_u(i,j) i=1..N(j), j=1..M is the upstream service rate of S_u i(j)
+#   global P_b;
+#   global P_s;
+#   global P;
+#   global b_u;
+#   global mu_d;
+#   global mu0;
+#   global blocking_type;
+
+#   blocking_type = blocking_type_;
+
+#   M = size(mu_, 2); ## Number of service centers
+#   size(C_) == [1,M] || error( "lee_et_al_98() - parameter C must be a (1xM) vector" );
+#   size(r_) == [M,M] || error( "lee_et_al_98() - parameter r must be a (MxM) matrix" );
+#   size(mu0_) == [1,M] || error( "lee_et_al_98() - parameter mu0 must be a (1,M) vector" );
+#   all( C_ > 0 ) || error( "lee_et_al_98() - parameter C must contain elements >0 only" );
+#   all( mu_ > 0 ) || error( "lee_et_al_98() - parameter mu must contain elements >0 only" );
+
+#   mu = [mu_ mu0_];
+#   mu0 = mu0_;
+#   C = C_;
+#   r = r_;
+
+#   ## Some constants
+#   global epsilon;
+#   global iter_count_max;
+
+#   ## Some constants
+#   C_max = max(C); # Maximum buffer size
+#   N_max = M+1;
+#   mu_d = mu_;
+#   P_b = zeros( M, N_max, M ); ## WARNING: the second index should start from 0, not 1!!
+#   P = zeros( C_max+N_max+1,M ); ## WARNING: the first index should start from 0m not 1!!
+#   mu_u = zeros( N_max, M );
+#   P_s = zeros( M );
+#   b_u = zeros( M, N_max+1, M ); ## WARNING: the second index should start from 0, not 1!!
+
+#   for j = 1:M
+#     for i = 1:N(j)
+#       k=f(i,j);
+#       if ( is_saturated(i,j) )
+#         mu_u(i,j) = mu(k);
+#       else
+#         mu_u(i,j) = mu(k) * r(k,j);
+#       endif
+#     endfor
+#   endfor
+
+#   ## End initialization step
+
+#   ## Iteration step
+#   iter_count = 1;
+
+#   do
+
+#     old_mu_u = mu_u;
+#     old_mu_d = mu_d;
+    
+#     ##  
+#     ## Step 1
+#     ##
+#     for j=1:M      
+
+#       P(1:C(j)+N(j)+1,j) = compute_P(j)';
+
+#       ##
+#       ## 1.1 Calculate P_s(j) using (3)    
+#       ##
+#       P_s( j ) = compute_P_s( j );
+      
+#       ##
+#       ## 1.2 Calculate mu_u(i,k) using (7) for all k \in D_j where f(i,k)=j
+#       ##
+#       for k=D(j)
+#         i = f_inverse_i(j,k);
+#         mu_u(i,k) = compute_mu_u(i,k);
+#       endfor    
+#     endfor
+    
+#     ## 
+#     ## Step 2
+#     ##
+#     for j=M:-1:1      
+
+#       ##P(1:C(j)+N(j)+1,j) = compute_P(j)';       
+
+#       ##
+#       ## 2.1 Calculate mu_d(j) using (5)
+#       ##
+#       mu_d(j) = compute_mu_d(j);
+      
+#       ##
+#       ## 2.2 Calculate P_b i(n:j) using (4) for n=0:N_j-1, i=1:N_j
+#       ##
+#       for i=1:N(j)        
+#         for n=1:N(j)
+#           b_u(i,n+1,j) = compute_b_u(i,n,j);
+#         endfor
+#       endfor
+#       for i=1:N(j)
+#         for n=0:N(j)-1
+#           P_b(i,n+1,j) = compute_P_b( i, n, j );
+#         endfor
+#       endfor      
+#     endfor
+    
+#     err1 = abs( old_mu_d - mu_d );
+#     err2 = abs( old_mu_u - mu_u );
+#     iter_count++;
+    
+#   until ( ( (err1 < epsilon) && (err2 < epsilon) ) || (iter_count > iter_count_max) );
+
+#   fprintf("Converged in %d iterations\n", iter_count );
+
+#   result = zeros( M, C_max+1 );
+#   for j=1:M
+#     ## P_j = compute_P(j);   
+#     result(j,[1:C(j)]) = P([1:C(j)],j)';
+#     result(j,C(j)+1) = sum( P([C(j)+1:C(j)+N(j)+1],j ) );
+#   endfor
+  
+# endfunction
+
+
+##############################################################################
+## usage: result = f( i, j )
+##
+## f(i,j) is the (scalar) index of the ith upstream server directly linked to
+## buffer B_j. This function is defined on p. 186 of [1]. Valid ranges
+## for the parameters are j=1..M, i=1..N(j). This function aborts on
+## parameters out of range.
+function result = f( i, j )
+  global r; # not modified
+  ( i>=1 && i <= N(j) ) || error( "f() i-index out of bound" );
+  ( j>=1 && j <= size(r,1)) || error( "f() j-index out of bound" );
+  result = U(j)(i);
+endfunction
+%!test
+%! global r mu0;
+%! r = [0 1 1 0; 0 0 1 1; 0 0 0 1; 1 0 0 0];
+%! mu0 = [1 0 1 0];
+%! assert( f(1,3), 7 );
+%! assert( f(2,3), 1 );
+%! assert( f(3,3), 2 );
+
+
+##############################################################################
+## Usage: result = is_saturated( i,j )
+##
+## Returns 1 iff S_u i(j) is a saturated server (i.e., if S_u i(j)
+## denotes an external arrival).
+function result = is_saturated(i,j)
+  global r mu0;
+  M = size(r,2);
+  if ( f(i,j) > M )
+    assert( mu0(f(i,j)-M) > 0 );
+    assert( i == 1 );
+    result = 1;
+  else
+    result = 0;
+  endif
+endfunction
+%!test
+%! global r mu0;
+%! r = [0 1 1 0; 0 0 1 1; 0 0 0 1; 1 0 0 0];
+%! mu0 = [1 0 1 0];
+%! assert( is_saturated(1,3), 1 );
+%! assert( is_saturated(1,1), 1 );
+%! assert( is_saturated(2,3), 0 );
+%! assert( is_saturated(3,3), 0 );
+
+
+##############################################################################
+## usage: result = f_inverse_i( k, j )
+##
+## Returns the (scalar) index i such that f(i,j) = k. That is, returns the
+## "position" of server S_k in the list of upstream servers of S_j.
+## Valid ranges for the parameters are k=1..M, j=1..M. This function
+## aborts on parameters out of range. It also aborts if no index i
+## exists such that f(i,j) = k.
+function result = f_inverse_i( k, j )
+  global r; # never modified
+  ( j>=1 && j<=size(r,1) ) || error( "f_inverse_i() - j parameter out of range" );
+  result = find( U(j) == k );
+  ( !isempty(result) ) || error( "f_inverse_i() - could not find inverse" );
+  ( f(result,j) == k ) || error( "f_inverse_i() - wrong result" );
+endfunction
+%!test
+%! global r mu0;
+%! r = [0 1 1 0; 0 0 1 1; 0 0 0 1; 1 0 0 0];
+%! mu0 = [1 0 1 0];
+%! assert( f_inverse_i(7,3), 1 );
+%! assert( f_inverse_i(1,3), 2 );
+%! assert( f_inverse_i(2,3), 3 );
+
+
+##############################################################################
+## usage: result = omega( n, j )
+##
+## Computes the scalar value \Omega_n(j), as defined on [1] p. 188. This
+## function examines the global variable blocking_type to determine
+## which variant of \Omega_n should be computed, and returns the
+## appropriate result.
+function result = omega( n, j )
+  if ( get_blocking_type(j) == 0 )
+    result = omega_BAS( n, j );
+  else
+    result = omega_RSB( n, j );
+  endif
+endfunction
+
+
+##############################################################################
+## usage: result = omega_prime( n, i, j )
+##
+## Computes the scalar value \Omega^i_n(j), as defined on [1] p. 189.
+## This function examines the global variable blocking_type to determine
+## which variant of \Omega^i_n should be computed, and returns the
+## appropriate result.
+function result = omega_prime( n, i, j )
+  if ( get_blocking_type(j) == 0 )
+    result = omega_prime_BAS( n, i, j );
+  else
+    result = omega_prime_RSB( n, i, j );
+  endif
+endfunction
+
+
+##############################################################################
+## usage: omega_BAS( n, j )
+##
+## Computes \Omega_n according to the upper part of Fig. 5, p. 188 on
+## [1]. This function implements the Blocking-After-Service version of
+## \Omega_n. Valid ranges for the parameter are n=0..N(j), j=1..M. This
+## function aborts on parameters out of range. Note that \Omega_0 is
+## defined to be 1, according to [2], p. 1125
+function result = omega_BAS( n, j )
+  global mu_u;
+
+  ( n>=0 && n<=N(j) ) || error( "omega_BAS() n parameter is invalid" );
+  ( j>=1 && j<=size(mu_u,2) ) || error( "omega_BAS() j parameter is invalid" );
+
+  if ( n == 0 )
+    result = 1;
+  else
+    combs = nchoosek( [1:N(j)], n );
+    el = mu_u( :, j )'; # Gets the array of elements of the j-th column
+    result = sum( prod( el( combs ), 2 ) );
+  endif
+endfunction
+%!test
+%! global mu_u r mu mu0;
+%! M=3;
+%! mu_u = 10*rand(M); # to amplify errors
+%! r = ones(M);
+%! mu = zeros(1,2*M);
+%! mu0 = zeros(1,M);
+%! expected_result = mu_u(1,2)*mu_u(2,2) + mu_u(1,2)*mu_u(3,2) + mu_u(2,2)*mu_u(3,2);
+%! assert(omega_BAS(2,2), expected_result);
+%! assert(omega_BAS(0,2),1);
+
+
+##############################################################################
+## usage: result = omega_prime_BAS( n, i, j )
+##
+## Computes \Omega^i_n for BAS blocking, according to [1], p. 189.
+function result = omega_prime_BAS( n, i, j )
+  global mu_u; # not modified
+
+  ( i>0 && i<=N(j) ) || error( "omega_prime_BAS() i-index out of range" );
+  ( j>0 && j<=size(mu_u,2) ) || error( "omega_prime_BAS() j-index out of range" );
+  ( n>=0 && n<N(j) ) || error( "omega_prime_BAS() n-index out of range" );
+
+  if ( n == 0 )
+    result = 1;
+  else
+    combs = nchoosek( [ 1:i-1 , i+1:N(j)], n );
+    el = mu_u( :, j )'; # Gets the array of elements of the j-th column
+    result = sum( prod( el( combs ), 2 ) );
+  endif
+endfunction
+%!test
+%! global mu_u r mu mu0;
+%! mu_u = 10*rand(3); # to aplify errors
+%! mu = zeros(1,6);
+%! mu0 = zeros(1,3);
+%! r = ones(3);
+%! M = 3;
+%! assert(omega_prime_BAS(2,2,2), prod(mu_u([1,3],2)) );
+%! assert(omega_prime_BAS(2,1,2), prod(mu_u([2,3],2)) );
+%! assert( omega_prime_BAS(0,1,1), 1 );
+
+%!test
+%! global mu_u r mu mu0;
+%! M=4;
+%! mu_u = rand(M);
+%! mu = zeros(1,2*M);
+%! mu0 = zeros(1,M);
+%! r = ones(M);
+%! assert( omega_prime_BAS(3,1,3), prod([ mu_u(2,3) mu_u(3,3) mu_u(4,3) ]) );
+
+
+##############################################################################
+## Usage: result = omega_RSB( n, j )
+##
+## Computes \Omega_n for Repetitive-Service blocking, according to [1],
+## fig. 6 p. 188.
+function result = omega_RSB( n, j )
+  global mu_u;
+
+  ( n>=0 && n<=N(j) ) || error( "omega_RSB() n parameter is invalid" );
+  ( j>0 && j<=size(mu_u,2) ) || error( "omega_RSB() j parameter is invalid" );
+
+  if ( n == 0 || n == N(j) ) ## FIEME???
+    result = 1;
+  else
+    combs = nchoosek( [2:N(j)], n );
+    el = (mu_u( :, j )'); # Gets the array of elements of the j-th column
+    result = sum( prod( el( combs ), 2 ) );
+  endif
+endfunction
+%!test
+%! global mu_u r mu mu0;
+%! M=4;
+%! mu_u = rand(M);
+%! mu = zeros(1,2*M);
+%! mu0 = zeros(1,M);
+%! r = ones(M);
+%! tmp = mu_u(:,2)';
+%! expected_result = sum( prod( tmp( [ 2 3; 2 4; 3 4 ] ), 2 ) );
+%! assert(omega_RSB(2,2), expected_result);
+%! assert(omega_RSB(0,2), 1);
+
+
+##############################################################################
+## Usage: result = omega_prima_RSB( n, i, j )
+##
+## Computes \Omega^i_n for Repetitive-Service blocking, according to
+## [1], p. 189. Note the following assumption: we assume that
+## omega_prime_RSB( i, B(j)-1, j ) == 1, even if the value of
+## omega_prime_RSB for this special case is not considered in [1].
+function result = omega_prime_RSB( n, i, j )
+  global mu_u;
+
+  ( i>0 && i<=N(j) ) || error( "omega_prime_RSB() i-index out of range" );
+  ( j>0 && j<=size(mu_u,2) ) || error( "omega_prime_RSB() j-index out of range" );
+  ( n>=0 && n<N(j) ) || error( "omega_prime_RSB() n-index out of range" );
+
+  if ( n == 0 || n == N(j)-1 ) ## FIXME???
+    result = 1;
+  else
+    combs = nchoosek( [ 2:i-1 , i+1:N(j)], n );
+    el = (mu_u( :, j )'); # Gets the array of elements of the j-th column
+    result = sum( prod( el( combs ), 2 ) );
+  endif
+endfunction
+%!test
+%! global mu_u r mu mu0;
+%! M=4;
+%! mu_u = rand(M);
+%! mu = zeros(1,2*M);
+%! mu0 = zeros(1,M);
+%! r = ones(M);
+%! tmp = mu_u(:,2)';
+%! expected_result = mu_u(2,2)*mu_u(3,2) +  mu_u(2,2)*mu_u(4,2) + mu_u(3,2)*mu_u(4,2);
+%! assert(omega_prime_RSB(2,1,2), expected_result);
+%! assert(omega_prime_RSB(2,2,2), mu_u(3,2)*mu_u(4,2) );
+%! assert(omega_prime_RSB(2,3,2), mu_u(2,2)*mu_u(4,2) );
+%! assert(omega_prime_RSB(0,1,2), 1);
+
+##############################################################################
+## Usage: compute_mu_u(i,j)
+##
+## Uses eq (7) from [1] to compute \mu_u i (j), i=1..M, j=1..M. The
+## result is a scalar value. WARNING: eq (7) is porbably wrong, as it is
+## different from the one used in Lemma 1, p. 191 [1]. Here we use
+## 1/mu_d(k) instead of 1/mu(k).
+function result = compute_mu_u( i, j )
+  global mu P_s r P_b mu_d mu_u;
+
+  k = f(i,j);
+  assert( k<=size(r,1) );
+  mu_star_k = sum( mu_u([1:N(k)],k) );
+
+#   result = r(k,j)/(P_s(k)/mu_star_k + 1/mu(k) -
+#                    r(k,j)*
+#                    sum( P_b(i,[1:N(j)],j) .* [1:N(j)]) / mu_d(j));
+
+  ## FIXME! Equation (7) is probably wrong. Check Lemma 1 p. 191
+  result = r(k,j)/(P_s(k)/mu_star_k + 1/mu_d(k) - r(k,j)*sum( P_b(i,[1:N(j)],j) .* [1:N(j)])/mu_d(j));
+endfunction
+
+
+##############################################################################
+## Usage: result = compute_mu_d(j)
+##
+## Uses eq. (5) from [1] to compute \mu_d (j), j=1..M. The result is a
+## scalar value. WARNING: Eq (5) is probably wrong: here we use sum(
+## P_b(k,[1:N(m)],m) .* [1:N(m)] ) instead of sum( P_b(k,[1:N(j)],m) .*
+## [1:N(j)] ).
+function result = compute_mu_d( j )
+  global mu_d P_b mu r;
+
+  ( j>0 && j <= size(r,1)) || error( "compute_mu_d() - j index out of bound" );
+
+  s = 0;
+  for m=D(j)
+    k = f_inverse_i(j,m);
+    ## s += r(j,m) * sum( P_b(k,[1:N(j)],m) .* [1:N(j)] ) / mu_d(m);
+
+    ## FIXME: Cambiato in accordo alla tesi di Bovino
+    s += r(j,m) * sum( P_b(k,[1:N(m)],m) .* [1:N(m)] ) / mu_d(m);
+  endfor
+  result = 1/( 1/mu(j) + s );
+  ## If j is an exit server, then result must be equal to mu(j)
+  if ( size( D(j), 2 ) == 0 )
+    assert( result, mu(j) );
+  endif
+endfunction
+
+
+##############################################################################
+## Usage: result = compute_P_s(j)
+##
+## Uses eq. (3) from [1] to compute P_s(j), j=1..M. P_s(j) is the
+## probability that S_d(j) is starved (i.e., is empty) at the service
+## completion instant.
+function result = compute_P_s( j )
+  global r P;
+  #P = compute_P(j);
+  M = size(r,2);
+  (j>=1 && j<=M) || error( "compute_P_s() - j index out of bound" );
+  result = P(2,j) / ( 1 - P(1,j) );
+endfunction
+
+
+##############################################################################
+## Usage: result = compute_P(j)
+##
+## Computes P(n:j) by solving the appropriate birth-death process. This
+## function returns a (1 x C(j)+N(j)+1 ) vector, where result(i) ==
+## P(i+1,j), i = 1..C(j)+N(j)+1
+function result = compute_P( j )
+  global C;
+  if ( get_blocking_type(j) == 0 )
+    result = compute_P_BAS(j);
+  else
+    result = compute_P_RSB(j);
+  endif
+  ## Check that successive marginal probabilities for nonblocking
+  ## states are equal each other. This is stated in [2], p. 1126
+  if ( C(j) >= 3 )
+    for i=0:C(j)-2
+      assert( result(i+2)/result(i+1), result(i+3)/result(i+2), 1e-4 );
+    endfor
+  endif
+endfunction
+
+
+##############################################################################
+## Usage: result = compute_P_BAS(j)
+##
+## Compute P(n,j) for each n=0..C(j)+N(j) by solving the birth-death
+## process. Returns a (1 x 1+C(j)+N(j)) vector
+function result = compute_P_BAS( j )
+  global mu_u mu_d C;
+  assert( get_blocking_type(j) == 0 );
+
+  ## Defines the transition probability matrix for the MC
+  mu_star_j = sum( mu_u([1:N(j)],j) );
+  assert( mu_star_j, sum( mu_u(:,j) ) );  
+
+  ## computes the steady-state probability
+  birth = zeros( 1, C(j)+N(j) );
+  death = mu_d(j) * ones( 1, C(j)+N(j) );
+  birth(1,[1:C(j)] ) = mu_star_j;
+  for i=1:N(j)
+    birth( 1, C(j)+i ) = i*omega_BAS(i,j)/omega_BAS(i-1,j);
+  endfor
+  result = ctmc_bd_solve( birth, death );
+endfunction
+
+
+##############################################################################
+## Usage: result = compute_P_RSB( j )
+##
+## Same as compute_P, for Repetitive-service blocking
+function result = compute_P_RSB( j )
+  global mu_u mu_d C;
+  assert( get_blocking_type(j) != 0 );
+
+  ## Defines the transition probability matrix for the MC
+  mu_star_j = sum( mu_u([1:N(j)],j) );
+  assert( mu_star_j, sum( mu_u(:,j) ) );  
+  
+  ## computes the steady-state probability
+  birth = zeros( 1, C(j)+N(j)-1 );
+  death = mu_d(j) * ones( 1, C(j)+N(j)-1 );
+  birth(1,[1:C(j)] ) = mu_star_j;
+  for i=1:N(j)-1
+    birth( 1, C(j)+i ) = i*omega_RSB(i,j)/omega_RSB(i-1,j);
+  endfor
+  result = [ ctmc_bd_solve( birth, death ) 0 ];
+endfunction
+
+
+##############################################################################
+## Usage: result = compute_P_b( i, n, j )
+##
+## Compute P_b i(n:j) using (4), where n=0..N(j)-1, i=1..N(j), j=1..M
+## The result is a scalar value.
+function result = compute_P_b( i,n,j )
+  global b_u C P;
+
+  ( n >= 0 && n < N(j) ) || error( "compute_P_b() - n index out of bound" );
+  ( i >= 1 && i <= N(j) ) || error( "compute_P_b() - i index out of bound" );
+  ##  P = compute_P(j);
+  M = size(C,2);
+
+  result = ( P(C(j)+n+1,j) - b_u(i,n+1,j) ) / ( 1 - sum( b_u( i, [2:(N(j)+1)], j ) ) );
+  ## FIXME: the next seems necessary
+#   if ( get_blocking_type(j) == 1 &&  n == N(j)-1 )
+#     assert( result, 0 );
+#   endif
+endfunction
+
+
+##############################################################################
+## Usage: result = compute_b_u(i,n,j)
+##
+## Compute b_u i(n:j) using (1), i=1..M, n=0..N(j), j=1..M. The result
+## is a single scalar element. According to [2], we let b_u i(0:j) = 0.
+function result = compute_b_u(i,n,j)
+  global mu_u C P;
+  M = size( C,2 );
+  ( i>0 && i<=M ) || error( "compute_b_u() - i index out of bound" );
+  ( j>0 && j<=M ) || error( "compute_b_u() - j index out of bound" );
+  ( n >= 0 && n <= N(j) ) || error( "compute_b_u() - n index out of bound" );
+  if ( n == 0 )
+    result = 0;
+  else
+    ## P = compute_P( j );
+    result = mu_u(i,j) * omega_prime( n-1,i,j ) / omega(n,j) * P(C(j)+n+1,j);
+  endif
+  if ( get_blocking_type(j) == 1 && n == N(j) )
+    assert( result, 0 );
+  endif
+endfunction
+
+
+##############################################################################
+## Usage: result = U( i )
+##
+## Returns a vector of the i-th element in the set U, that is, returns
+## the index of the upstream servers for S_i, i=1..M
+##
+## @param r the MxM routing matrix
+##
+## @param mu0 the vector of external arrivals
+function result = U( i )
+  global r mu0;
+  M = size(r,2);
+  ( i>0 && i<=M ) || error( "U() - i index out of bound" );
+  result = find( r(:,i) > 0 )';
+  if ( mu0( i ) > 0 )
+    result = [ M+i result ];
+  endif
+endfunction
+%!test
+%! global r mu0;
+%! r = [0 1 1 0; 0 0 1 1; 0 0 0 1; 1 0 0 0];
+%! mu0 = [1 0 1 0];
+%! assert( U(1), [5 4] );
+%! assert( U(3), [7 1 2] );
+
+
+##############################################################################
+## Usage: result = D( i )
+##
+## Given the routing matrix r, computes the downstream index set D_i.
+## D_i is defined as the set of indexes of downstream servers directly
+## connected with S_i.
+function result = D( i )
+  global r;
+  ( i>0 && i<=size(r,1) ) || error( "D() - i index out of bound" );
+  result = find( r(i,:) > 0 );
+endfunction
+%!test
+%! global r;
+%! r = [0 1 1 0; 0 0 1 1; 0 0 0 1; 1 0 0 0];
+%! assert( D(2), [3 4] );
+
+
+##############################################################################
+## Usage: result = N( i )
+##
+## Returns a scalar representing the number of upstream servers of S_i,
+## that is, returns the number of elements in U(i); i=1..M
+function result = N( i )
+  global r;
+  ( (i>0) && (i<=size(r,1)) ) || error( "N() - i index out of bound" );
+  result = size( U(i), 2 );
+endfunction
+%!test
+%! global r mu0;
+%! r = [0 1 1 0; 0 0 1 1; 0 0 0 1; 1 0 0 0];
+%! mu0 = [1 0 1 0];
+%! assert( N(1), 2 );
+%! assert( N(3), 3 );
+
+
+##############################################################################
+## Usage: result = get_blocking_type( j )
+##
+## Returns the blocking type for server S_u1(j); result == 0 means BAS,
+## result == 1 means RSB.
+function result = get_blocking_type( j )
+  global r mu0 blocking_type;
+  M = size(r,2);
+  ## If blocking_type(j) != 0 and mu0(j) > 0, then result == 1
+  ## (Repetitive-Service Blocking). Otherwise, result == 0 (BAS)
+  ( j >= 1 && j <= size(mu0,2) ) || error( "get_blocking_type() - j \
+      parameter out of bounds" );
+  if ( f(1,j) <= M )
+    result = 0; # non-saturated servers are always BAS
+    return
+  endif
+  k = f(1,j) - M;
+  assert( mu0(k) > 0 );
+  if ( blocking_type(k) != 0 )
+    result = 1; # repetitive-service blocking
+  else
+    result = 0; # BAS
+  endif
+endfunction
+%!test
+%! global r mu0 blocking_type;
+%! r = [0 1 1 0; 0 0 1 1; 0 0 0 1; 1 0 0 0];
+%! mu0 = [1 0 1 0];
+%! blocking_type = [1 0 1 0];
+%! assert( get_blocking_type(1), 1 );
+%! assert( get_blocking_type(2), 0 );
+%! assert( get_blocking_type(3), 1 );
+%! assert( get_blocking_type(4), 0 );
+
+# %!test
+# %! r = [0 0.35 0.35; 0 0 0.65; 0 0 0];
+# %! C = [ 2 2 3 ];
+# %! mu = [ 2 1.5 1 ];
+# %! mu0 = [ 1.2 0.3 0.2 ];
+# %! P1 = lee_et_al_98( mu, mu0, C, r, [ 1 1 1 ] );
+# %! P2 = lee_et_al_98_acyclic( mu, mu0, C, r, [ 1 1 1 ] );
+# %! assert( P1, P2, 1e-4 );
+
+
+##############################################################################
+## Usage: compute_X_u(i,j)
+##
+## Computes X_ui(j) using eq. (10) from Lee et al. [1]
+function result = compute_X_u(i,j)
+  global P_b mu_d mu_u;
+  result = 1/( 1/mu_u(i,j) + 1/mu_d(j) * dot( P_b(i,[1:N(j)],j), [1:N(j)] ) );
+endfunction
+
+
+##############################################################################
+## Usage: compute_X_d(k)
+##
+## Computes X_d(k) using eq. (9) from Lee et al. [1]
+function result = compute_X_d(k)
+  global P_s mu_d mu_u;
+  mu_star_k = sum(mu_u([1:N(k)],k));
+  result = 1/( 1/mu_d(k) + P_s(k)/mu_star_k );
+endfunction
+
+
+##############################################################################
+## Usage: print_header()
+##
+## Prints the header used for the demo results
+function print_header()
+  printf("%10s\t%5s\t%5s %5s\t%5s %5s %4s\n", \
+         "Param", "Exact", "This", "Err%", "Paper", "Err%", "Res" );
+endfunction
+
+
+##############################################################################
+## Usage: compare( param, simulation, result, paper)
+##
+## This is a function used in the demos
+##
+## @param param The string representing the parameter name
+##
+## @param simulation The simulation result shown in the paper
+## 
+## @param result The result computed by this implementation
+##
+## @param paper The result copmputed by the algorithm shown in the paper
+function compare( param, simulation, result, paper )
+  
+  err_res = ( result - simulation ) / simulation * 100;
+  err_pap = ( paper - simulation ) / simulation * 100;
+  tolerance = 1e-3;
+
+  if ( abs( result - paper ) < tolerance )
+    test_result = "PASS";
+  else
+    test_result = "FAIL";
+  endif
+  printf("%10s\t%5.3f\t%5.3f %5.1f\t%5.3f %5.1f %4s\n", param, simulation, result, err_res, paper, err_pap, test_result );
+
+endfunction
+
+##############################################################################
+### 
+### Start "real" tests 
+###
+
+%!demo
+%! disp("Table V, p. 1130 Lee & Pollock [2]");
+%! r = [ 0 0.4 0.4; \
+%!       0 0   0.7; \
+%!       0 0   0    ];
+%! C = [ 20 2 2 ];
+%! mu = [ 2 2 2 ];
+%! mu0 = [ 1.5 0 0 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [ 1 0 0 ] );
+%! assert( get_blocking_type(1), 1 );
+%! assert( get_blocking_type(2), 0 );
+%! assert( get_blocking_type(3), 0 );
+%! assert( f(1,1), 4 );
+%! assert( f(1,2), 1 );
+%! assert( f(1,3), 1 );
+%! assert( f(2,3), 2 );
+%! print_header();
+%! compare( "P_1(0)", 0.1560, result(1,1), 0.1516 );
+%! compare( "P_1(1)", 0.1297, result(1,2), 0.1287 );
+%! compare( "P_1(2)", 0.1085, result(1,3), 0.1091 );
+%! compare( "P_1(3)", 0.0914, result(1,4), 0.0926 );
+%! compare( "P_1(4)", 0.0772, result(1,5), 0.0786 );
+%! compare( "P_1(5)", 0.0654, result(1,6), 0.0666 );
+%! compare( "P_2(0)", 0.6557, result(2,1), 0.6473 );
+%! compare( "P_2(1)", 0.2349, result(2,2), 0.2357 );
+%! compare( "P_2(2)", 0.1094, result(2,3), 0.1170 );
+%! compare( "P_3(0)", 0.4901, result(3,1), 0.4900 );
+%! compare( "P_3(1)", 0.2667, result(3,2), 0.2662 );
+%! compare( "P_3(2)", 0.2431, result(3,3), 0.2438 );
+
+%!demo
+%! disp("Table IV, p. 1130 Lee & Pollock [2]");
+%! r = [ 0 0.4 0.4; \
+%!       0 0   0.5; \
+%!       0 0   0 ];
+%! C = [ 1 1 1 ];
+%! mu = [ 1 1 1 ];
+%! mu0 = [ 3.0 0 0 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [ 1 0 0 ] );
+%! print_header();
+%! compare( "P_1(0)", 0.2154, result(1,1), 0.2092 );
+%! compare( "P_1(1)", 0.7846, result(1,2), 0.7909 );
+%! compare( "P_2(0)", 0.7051, result(2,1), 0.6968 );
+%! compare( "P_2(1)", 0.2949, result(2,2), 0.3032 );
+%! compare( "P_3(0)", 0.6123, result(3,1), 0.6235 );
+%! compare( "P_3(1)", 0.3877, result(3,2), 0.3765 );
+
+%!demo
+%! disp("Table X first group, p 1132 Lee & Pollock [2]");
+%! r = [0 0.35 0.35; \
+%!      0 0    0.65; \
+%!      0 0    0 ];
+%! C = [ 5 4 3 ];
+%! mu = [ 1 1 1 ];
+%! mu0 = [ 2 0.2 0.1 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [ 1 1 1 ] )
+%! print_header();
+%! compare( "P_1(5)", 0.558, result(1,6), 0.558 );
+%! compare( "P_2(4)", 0.074, result(2,5), 0.074 );
+%! compare( "P_3(3)", 0.279, result(3,4), 0.282 );
+
+%!demo
+%! disp("Table X, second group, p. 1132 Lee & Pollock [2]");
+%! r = [0 0.35 0.35; \
+%!      0 0    0.65; \
+%!      0 0    0 ];
+%! C = [ 2 2 3 ];
+%! mu = [ 2 1.5 1 ];
+%! mu0 = [ 1.2 0.3 0.2 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [ 1 1 1 ] )
+%! print_header();
+%! compare( "P_1(2)", 0.269, result(1,3), 0.272 );
+%! compare( "P_2(2)", 0.193, result(2,3), 0.206 );
+%! compare( "P_3(3)", 0.374, result(3,4), 0.391 );
+
+%!demo
+%! disp("Table 1 first group, p. 195 Lee & al. [1]");
+%! r = [0   1   0; \
+%!      0   0   1; \
+%!      0.1 0   0  ];
+%! C = [ 2 2 2 ];
+%! mu = [ 1 1 1 ];
+%! mu0 = [ 1 0 0 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [ 1 0 0 ] )
+%! print_header();
+%! compare( "P_1(0)", 0.206, result(1,1), 0.210 );
+%! compare( "P_1(2)", 0.458, result(1,3), 0.483 );
+%! compare( "P_2(0)", 0.265, result(2,1), 0.287 );
+%! compare( "P_2(2)", 0.450, result(2,3), 0.452 );
+%! compare( "P_3(0)", 0.376, result(3,1), 0.389 );
+%! compare( "P_3(2)", 0.334, result(3,3), 0.335 );
+
+%!demo
+%! disp("Table 1, second group, p. 195 Lee & al. [1]");
+%! r = [0   1   0; \
+%!      0   0   1; \
+%!      0.1 0   0  ];
+%! C = [ 1 1 1 ];
+%! mu = [ 1.5 1.5 1.5 ];
+%! mu0 = [ 1 0 0 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [ 1 0 0 ] )
+%! print_header();
+%! compare( "P_1(0)", 0.510, result(1,1), 0.478 );
+%! compare( "P_1(1)", 0.490, result(1,2), 0.522 );
+%! compare( "P_2(0)", 0.526, result(2,1), 0.532 );
+%! compare( "P_2(1)", 0.474, result(2,2), 0.468 );
+%! compare( "P_3(0)", 0.610, result(3,1), 0.620 );
+%! compare( "P_3(1)", 0.390, result(3,2), 0.380 );
+
+%!demo
+%! disp("Table 2, first group, p. 196 Lee & al. [1]");
+%! r = [0   0.2 0.2 0.3;   \
+%!      0   0   0.6 0.3; \
+%!      0   0   0   0.8; \
+%!      0.1 0   0   0    ];
+%! C = [ 1 1 1 1 ];
+%! mu = [ 1 0.5 0.5 1 ];
+%! mu0 = [ 1 0.2 0.2 0 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [ 1 1 1 0 ] )
+%! print_header();
+%! compare( "P_1(0)", 0.364, result(1,1), 0.338 );
+%! compare( "P_1(1)", 0.636, result(1,2), 0.662 );
+%! compare( "P_2(0)", 0.490, result(2,1), 0.477 );
+%! compare( "P_2(1)", 0.510, result(2,2), 0.523 );
+%! compare( "P_3(0)", 0.389, result(3,1), 0.394 );
+%! compare( "P_3(1)", 0.611, result(3,2), 0.606 );
+%! compare( "P_4(0)", 0.589, result(4,1), 0.589 );
+%! compare( "P_4(1)", 0.411, result(4,2), 0.411 );
+
+%!demo
+%! disp("Table 3, left column, p. 197 Lee & al. [1]");
+%! r = [0   0.3 0   0   0.3 0   0   0.4;   \
+%!      0   0   1   0   0   0   0   0; \
+%!      0   0   0   1   0   0   0   0; \
+%!      0   0.1 0   0   0   0   0   0.9; \
+%!      0   0   0   0   0   1   0   0; \
+%!      0   0   0   0   0   0   1   0; \
+%!      0   0   0   0   0.1 0   0   0.9; \
+%!      0   0   0   0   0   0   0   0 ];
+%! C = [ 2 2 2 2 2 2 2 2 ];
+%! mu = [ 1.5 1 1 1 1 1 1 1.5 ];
+%! mu0 = [ 1 0.2 0 0 0.2 0 0 0 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [ 0 0 0 0 0 0 0 0] )
+%! global mu_d;
+%! assert( mu_d(8), mu(8) );
+%! print_header();
+%! compare( "P_1(0)", 0.220, result(1,1), 0.221 );
+%! compare( "P_1(2)", 0.547, result(1,3), 0.539 );
+%! compare( "P_2(0)", 0.434, result(2,1), 0.426 );
+%! compare( "P_2(2)", 0.306, result(2,3), 0.309 );
+%! compare( "P_3(0)", 0.415, result(3,1), 0.406 );
+%! compare( "P_3(2)", 0.315, result(3,3), 0.318 );
+%! compare( "P_4(0)", 0.377, result(4,1), 0.373 );
+%! compare( "P_4(2)", 0.348, result(4,3), 0.352 );
+%! compare( "P_5(0)", 0.434, result(5,1), 0.426 );
+%! compare( "P_5(2)", 0.305, result(5,3), 0.309 );
+%! compare( "P_6(0)", 0.416, result(6,1), 0.406 );
+%! compare( "P_6(2)", 0.315, result(6,3), 0.318 );
+%! compare( "P_7(0)", 0.376, result(7,1), 0.373 );
+%! compare( "P_7(2)", 0.363, result(7,3), 0.352 );
+%! compare( "P_8(0)", 0.280, result(8,1), 0.274 );
+%! compare( "P_8(2)", 0.492, result(8,3), 0.492 );
+
+##############################################################################
+##
+## This is the first bunch of tests. Here we compare the result from the
+## Lee et al. algorithm with those obtained from the (exact) MVA for
+## open, single class networks. Assuming that there is enough buffer
+## space in the original network, the result from Lee et al and MVA
+## should be almost exactly the same.
+
+%!test
+%! #printf("Simple tandem network with enough buffer space");
+%! r = [ 0 1 0; \
+%!       0 0 1; \
+%!       0.1 0 0];
+%! C = 20 * ones(1,3);
+%! mu=[ 2 2 2 ];
+%! mu0=[ 1 0 0 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [1 0 0] );
+%! Q_lee = zeros(1,3);
+%! [U R Q_mva] = qnopen( 1, 1 ./ mu, qnvisits(r, mu0) );
+%! for i=1:3
+%!   Q_lee(i) = dot( result(i,:), [0:C(i)] );
+%!   #printf("Q(%d) Lee=%5.3f MVA=%5.3f\n", i, Q_lee(i), Q_mva(i) );
+%!   assert( Q_lee(i), Q_mva(i), 1e-4 );
+%! endfor
+
+%!test
+%! #printf("Table 2, third group, p. 196 Lee & al. [1], enough buffer space");
+%! r = [0   0.2 0.2 0.3;   \
+%!      0   0   0.6 0.3; \
+%!      0   0   0   0.8; \
+%!      0.1 0   0   0    ];
+%! C = 20*ones(1,4);
+%! mu = [ 2 1 1 2 ];
+%! mu0 = [ 1 0.2 0.2 0 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [ 1 1 1 0 ] );
+%! Q_lee = zeros(1,4);
+%! [U R Q_mva] = qnopen( 1, 1 ./ mu, qnvisits(r, mu0 ) );
+%! for i=1:4
+%!   Q_lee(i) = dot( result(i,:), [0:C(i)] );
+%!   #printf("Q(%d) Lee=%5.3f MVA=%5.3f\n", i, Q_lee(i), Q_mva(i) );
+%!   assert( Q_lee(i), Q_mva(i), 1e-2 );
+%! endfor
+
+%!test
+%! #printf("Table 3, right column, p. 197 Lee & al. [1], enough buffer space");
+%! r = [0   0.3 0   0   0.3 0   0   0.4;   \
+%!      0   0   1   0   0   0   0   0; \
+%!      0   0   0   1   0   0   0   0; \
+%!      0   0.1 0   0   0   0   0   0.9; \
+%!      0   0   0   0   0   1   0   0; \
+%!      0   0   0   0   0   0   1   0; \
+%!      0   0   0   0   0.1 0   0   0.9; \
+%!      0   0   0   0   0   0   0   0 ];
+%! C = 20*ones(1,8);
+%! mu = [ 2 1.5 1.5 1.5 1.5 1.5 1.5 2 ];
+%! mu0 = [ 1 0.2 0 0 0.2 0 0 0 ];
+%! result = lee_et_al_98( mu, mu0, C, r, [0 0 0 0 0 0 0 0] );
+%! Q_lee = zeros(1,8);
+%! [U R Q_mva] = qnopen( 1, 1./ mu, qnvisits(r, mu0) );
+%! for i=1:8
+%!   Q_lee(i) = dot( result(i,:), [0:C(i)] );
+%!   #printf("Q(%d) Lee=%5.3f MVA=%5.3f\n", i, Q_lee(i), Q_mva(i) );
+%!   assert( Q_lee(i), Q_mva(i), 1e-2 );
+%! endfor
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/broken/qnmmmk_alt.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,162 @@
+## Copyright (C) 2009 Dmitry Kolesnikov
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U} @var{R} @var{Q} @var{X} @var{p0} @var{pK}] =} qnmmmk_alt (@var{lambda}, @var{mu}, @var{m}, @var{K} )
+##
+## @cindex @math{M/M/m/K} system
+##
+## Compute utilization, response time, average number of requests and
+## throughput for a @math{M/M/m/K} finite capacity system. In a
+## @math{M/M/m/K} queue there are @math{m \geq 1} service centers. At any
+## moment, at most @math{K} requests can be in the system, @math{K
+## \geq m}.
+##
+## @iftex
+##
+## The steady-state probability @math{\pi_k} that there are @math{k}
+## jobs in the system, @math{0 \leq k \leq K} can be expressed as:
+##
+## @tex
+## $$
+## \pi_k = \cases{ \displaystyle{{\rho^k \over k!} \pi_0} & if $0 \leq k \leq m$;\cr
+##                 \displaystyle{{\rho^m \over m!} \left( \rho \over m \right)^{k-m} \pi_0} & if $m < k \leq K$\cr}
+## $$
+## @end tex
+##
+## where @math{\rho = \lambda/\mu} is the offered load. The probability
+## @math{\pi_0} that the system is empty can be computed by considering
+## that all probabilities must sum to one: @math{\sum_{k=0}^K \pi_k = 1},
+## which gives:
+##
+## @tex
+## $$
+## \pi_0 = \left[ \sum_{k=0}^m {\rho^k \over k!} + {\rho^m \over m!} \sum_{k=m+1}^K \left( {\rho \over m}\right)^{k-m} \right]^{-1}
+## $$
+## @end tex
+##
+## @end iftex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+##
+## Arrival rate (jobs/@math{s}, @code{@var{lambda}>0}).
+##
+## @item mu
+##
+## Service rate (jobs/@math{s}, @code{@var{mu}>0}).
+##
+## @item m
+##
+## Number of servers (@code{@var{m}>=1}).
+##
+## @item k
+##
+## Maximum number of requests allowed in the system (@code{@var{k} >= @var{m}}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+##
+## Service center utilization
+##
+## @item R
+##
+## Service center response time
+##
+## @item Q
+##
+## Average number of requests in the system
+##
+## @item X
+##
+## Service center throughput
+##
+## @item p0
+##
+## Probability that there are no requests in the system.
+##
+## @item pK
+##
+## Probability that there are @math{K} requests in the system (i.e., 
+## probability that the system is full).
+##
+## @end table
+##
+## @var{lambda}, @var{mu}, @var{m} and @var{K} can be vectors of the
+## same size. In this case, the results will be vectors as well.
+##
+## @seealso{qnmm1,qnmminf,qnmmm}
+##
+## @end deftypefn
+
+## Author: Dmitry Kolesnikov
+function [U R Q X p0 pm pk] = qnmmmk_alt( lambda, mu, m, k)
+  if ( nargin != 4 )
+    print_usage();
+  endif
+  [err lambda mu m k] = common_size( lambda, mu, m, k );
+  if ( err ) 
+    error( "Parameters are not of common size" );
+  endif
+
+  ( isvector(lambda) && isvector(mu) && isvector(m) && isvector(k) ) || ...
+      error( "lambda, mu, m, k must be vectors of the same size" );
+  all( k>0 ) || \
+      error( "k must be strictly positive" );
+  all( m>0 ) && all( m <= k ) || \
+      error( "m must be in the range 1:k" );
+  all( lambda>0 ) && all( mu>0 ) || \
+      error( "lambda and mu must be >0" );
+  
+  rho = lambda ./ mu;
+  i1 = 0:m; # range
+  i2 = (m+1):k; # range
+  p0 = 1 / ( sum( rho .^ i1 ./ factorial(i1) ) + rho^m/factorial(m)*sum( (rho/m).^(i2-m) ) );
+  pm = p0 * (rho)^m / factorial(m);
+  pk = p0 * rho^k / (factorial(m) * m^(k-m));
+  Q = (sum( i1 .* rho .^ i1 ./ factorial(i1) ) + ...
+       rho^m/factorial(m)*sum( i2.*((rho/m).^(i2-m)) ))*p0;
+  X = lambda*(1-pk);
+  U = X ./ (m .* mu );
+  R = Q./X;
+
+endfunction
+%!test
+%! lambda = 0.8;
+%! mu = 0.85;
+%! k = 5;
+%! [U1 R1 Q1 X1 p01 pm pk1] = qnmmmk_alt( lambda, mu, 1, k );
+%! [U2 R2 Q2 X2 p02 pk2] = qnmm1k( lambda, mu, k );
+%! assert( [U1 R1 Q1 X1 p01 pk1], [U2 R2 Q2 X2 p02 pk2], 1e-5 );
+
+%!test
+%! lambda = 0.8;
+%! mu = 0.85;
+%! m = 3;
+%! k = 5;
+%! [U1 R1 Q1 X1 p01] = qnmmmk_alt( lambda, mu, m, k );
+%! [U2 R2 Q2 X2 p02] = qnmmmk( lambda, mu, m, k );
+%! assert( [U1 R1 Q1 X1 p01], [U2 R2 Q2 X2 p02], 1e-5 );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/broken/qnopenmultig.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,238 @@
+## Copyright (C) 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnopenmultig (@var{lambda}, @var{c2lambda}. @var{mu}, @var{c2mu}, @var{P})
+##
+## Approximate analysis of open, multiple-class networks with general
+## arrival and service time distributions (@code{qnopenmultig} stands
+## for "OPEN, MULTIclass, General"). Single server and multiple server
+## nodes are supported. This function assumes a network with @math{K}
+## service centers and @math{C} customer classes.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## @code{@var{lambda}(c, k)} is the external
+## arrival rate of class @math{c} customers at center @math{k}.
+##
+## @item c2lambda
+## @code{@var{c2lambda}(c, k)} is the squared coefficient of variation of
+## class @math{c} arrival rate at center @math{k}
+##
+## @item mu
+## @code{@var{mu}(c,k)} is the mean service rate of class @math{c}
+## customers on the service center @math{k} (@code{@var{S}(c,k)>0}).
+##
+## @item c2mu
+## @code{@var{c2mu}(c,k)} is the squared Coefficient of variation of class
+## @math{c} service rate at center @math{k}
+##
+## @item P
+## @code{@var{P}(r,i,s,j)} is the probability that a class @math{r}
+## request which completes service at center @math{i} is routed
+## to center @math{j} as class @math{s} request. @strong{Class
+## switching is not supported}: therefore, @math{P(r,i,s,j) = 0}
+## if @math{r \neq s}.
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at service center
+## @math{k}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## @code{@var{U}(c,k)} is the
+## class @math{c} utilization of center @math{k}.
+##
+## @item R
+## @code{@var{R}(c,k)} is the class @math{c} response time at
+## center @math{k}. The system response time for
+## class @math{c} requests can be computed
+## as @code{dot(@var{R}, @var{V}, 2)}.
+##
+## @item Q
+## @code{@var{Q}(c,k)} is the average number of class @math{c} requests
+## at center @math{k}. The average number of class @math{c} requests
+## in the system @var{Qc} can be computed as @code{Qc = sum(@var{Q}, 2)}
+##
+## @item X
+## @code{@var{X}(c,k)} is the class @math{c} throughput
+## at center @math{k}.
+##
+## @end table
+##
+## @seealso{qnopen,qnopensingle,qnvisits}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+function [U R Q X] = qnopenmultig( l0, c20, mu, c2, P, m )
+
+  error("This function is not fully implemented yet");
+
+  if ( nargin < 5 || nargin > 6 )
+    print_usage();
+  endif
+
+  ismatrix(l0) || \
+      usage("lambda must be a matrix");
+  
+  C = rows(l0);
+  K = columns(l0);
+
+  size(c20) == size(l0) || \
+      usage("c20 must have the same size of lambda");
+
+  size(mu) == size(l0) || \
+      usage("mu must have the same size of lambda");
+
+  size(c2) == size(l0) || \
+      usage("c2 must have the same size of lambda");
+
+  ismatrix(P) && ndims(P) == 4 && [C,K,C,K] == size(P) || \
+      usage("Wrong size of matrix P (I was expecting %dx%dx%dx%d)", C, K, C, K);
+
+  if ( nargin < 6 )
+    m = ones(1,K);
+  else
+    ( isvector( m ) && length(m) == K && all( m >= 1 ) ) || \
+        usage( "m must be >= 1" );
+    m = m(:)'; # make m a row vector
+  endif
+
+  ## Class switching is not allowed. Therefore, to be compliant with the
+  ## reference above (Bolch et al.), we drop one dimension from matrix P
+  P_ijr = zeros(K,K,C);
+  for r=1:C
+    P_ijr(:,:,r) = P(r,:,r,:);
+  endfor
+#{
+  for i=1:K
+    for j=1:K
+      for r=1:C
+        P_ijr(i,j,r) = P(r,i,r,j);
+      endfor
+    endfor
+  endfor
+#}
+
+  l = sum(sum(l0)) .* qnvisits(P,l0);
+
+  c2A_ir = c2D_ir = zeros(K,C);
+  c2A_i_old = c2A_i = c2B_i = c2D_i = zeros(1,K);
+  c2_ijr = ones(K,K,C);
+
+  ## the implementation that follows is based on Section 10.1.3 (p.
+  ## 439--441) of Bolch et al. In order to be facilitate debugging, we
+  ## make ourselves compliant with their notation. This means that class
+  ## and server indexes need to be swapped, e.g., they use rho(i,r)
+  ## instead of rho(r,i) to denote the class-r utilization on center i.
+
+  lambda_ir = l';
+  lambda0_ir = l0';
+  c20_ir = c20';
+  c2B_i = c2_ir = c2';
+  mu_ir = mu';
+
+  lambda_i = sum(lambda_ir,2); # lambda_i(i) is the total arrival rate at center i
+
+  rho_ir = zeros(K,C); # rho_ir(i,r) is the class r utilization at center i
+  for r=1:C # FIXME: parallelize this
+    for i=1:K
+      rho_ir(i,r) = lambda_ir(i,r) / ( m(i) * mu_ir(i,r) );
+    endfor
+  endfor
+
+  rho_i = sum(rho_ir,2); # rho_i(i) is the utilization of center i
+
+  mu_i = zeros(1,K); # mu_i(i) is the mean service rate at center i
+  for i=1:K
+    mu_i(i) = 1 / sum( rho_ir(i,:) ./ lambda_i(i) );
+  endfor
+
+  do 
+
+    c2A_i_old = c2A_i;
+
+    ## Decomposition of Whitt
+    
+    ## Merging
+    for r=1:C
+      for i=1:K
+        c2A_ir(i,r) = 1 / lambda_ir(i,r) * ...
+            (c20_ir(i,r)*lambda0_ir(i,r) + ...
+             sum( c2_ijr(:,i,r) .* lambda_ir(:,r) .* P_ijr(:,i,r) ) );
+      endfor
+    endfor
+    
+    for i=1:K
+      c2A_i(i) = 1 / lambda_i(i) * sum(c2A_ir(i,:) .* lambda_ir(i,:) );
+    endfor
+    
+    ## Coeff of variation
+
+    for i=1:K
+      c2B_i(i) = -1 + sum( lambda_ir(i,:)/lambda_i(i) .* ...
+                          ( mu_i(i) / (m(i) * mu_ir(i,:))).^2 .* ...
+                          (c2_ir(i,:)+1) );
+    endfor
+
+    ## Flow (Pujolle)
+    for i=1:K
+      c2D_i(i) = 1 + (rho_i(i)^2 * (c2B_i(i) - 1) / sqrt(m(i))) + ...
+          (1 - rho_i(i)^2)*(c2A_i(i) - 1);
+#{
+      c2D_i(i) = rho_i(i)^2 * (c2B_i(i)+1) +...
+          (1-rho_i(i))*c2A_i(i) + ...
+          rho_i(i)*(1-2*rho_i(i));
+#}
+    endfor
+
+    ## Splitting
+    for i=1:K
+      for j=1:K
+        for r=1:C
+          c2_ijr(i,j,r) = 1 + P_ijr(i,j,r) * (c2D_i(i) - 1);
+        endfor
+      endfor
+    endfor
+
+  until ( norm(c2A_i - c2A_i_old, "inf") < 1e-3 );
+
+endfunction
+
+%!demo
+%! C = 1; K = 4;
+%! P=zeros(C,K,C,K);
+%! P(1,1,1,2) = .5;
+%! P(1,1,1,3) = .5;
+%! P(1,3,1,1) = .6;
+%! P(1,2,1,1) = P(1,4,1,1) = 1;
+%! mu = [25 33.333 16.666 20];
+%! c2B = [2 6 .5 .2];
+%! c20 = [0 0 0 4];
+%! lambda = [0 0 0 4];
+%! qnwhitt(lambda, c20, mu, c2B, P );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/broken/qnopensinglenexp.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,100 @@
+## Copyright (C) 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnopensinglenexp (@var{lambda}, @var{S}, @var{V}) 
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnopensingle (@var{lambda}, @var{S}, @var{V}, @var{m})
+##
+## Approximate analysis of open queueing networks
+## with general arrival and service time distributions.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## @code¬@var{lambda}(i,r)} is the external arrival rate of class-@math{r}
+## requests at service center @math{i}.
+##
+## @item P
+## @code{@var{P}(i,j,r)} is the probability that a class-@math{r}
+## request moves to center @math{j} after completing service at center
+## @math{i}.
+##
+## @item mu
+## mu(i,r) service rate of class-r jobs at center i
+##
+## @item m
+## m(i) is the number of servers at center i
+##
+## @end table
+##
+## Reference: Bolch et al., p. 439-444.
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+##
+## WORK IN PROGRESS: DO NOT USE!!
+##
+function qnopensinglenexp( l0, c20ir, P, mu, m, c2ir )
+
+  error("This function is work in progress. Do not use it yet");
+
+  N = size(l0,1); # number of service centers
+  R = size(l0,2); # number of classes
+  lir = zeros(N, R); # arrival rate to node i of class r
+  for r=1:R # FIXME: avoid loop
+    lir(:,r) = l0(:,r) \ ( eye(N) - P(:,:,r) );
+  endfor
+  lijr = zeros(N, N, R); # arrival rate from node i to node j of class r
+  for i=1:N # FIXME: avoid loop
+    for j=1:N
+      for r=1:R
+	lijr(i,j,r) = lir(i,t)*P(i,j,r);
+      endfor
+    endfor
+  endfor
+  li = sum(lir,2); # arrival rate to node i
+  roir = zeros(N,R); # utilization of node i due to customers of class r
+  for i=1:N
+    for r=1:R
+      roir(i,r) = lir(i,r) / (m(i)*mu(i,r));
+    endfor
+  endfor
+  roi = sum(roir,2); # utilization of node i
+  mui = zeros(1,N); # mean service rate of node i
+  for i=1:N
+    mui(i) = 1 / ( sum( lir(i,:) ./ li(i) ./ (m(i) .* muir(i,:)) ) );
+  endfor
+  c2Bi = zeros(1,N); # squared coefficient of variation of service time of node i
+  for i=1:N
+    c2Bi(i) = -1 + sum( lir(i,:) ./ li(i) .* (mu(i) ./ (m(i) .* mu(i,:)).^2 .* c2ir(i,:) + 1 ) );
+  endfor
+  c2ijr = ones(N, N, R);
+  c2Air = zeros(N,R);
+  c2Ai = c2Di = zeros(1,N);
+  while 1==1
+    ## merging
+    c2Air(i,:) = 1./lir(i,:) .* sum( c2ijr(j,i,:)
+    ## flow
+  endwhile
+		      
+endfunction
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/Makefile	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,42 @@
+DOC=queueing
+CHAPTERS=summary.texi installation.texi markovchains.texi singlestation.texi queueingnetworks.texi conf.texi ack.texi contributing.texi gpl.texi gettingstarted.texi
+DISTFILES=$(wildcard *.txi) Makefile $(DOC).pdf $(DOC).html $(DOC).texi
+
+.PHONY: clean
+
+ALL: pdf html ../INSTALL
+
+html: $(DOC).html
+
+pdf: $(DOC).pdf
+
+info: $(DOC).info
+
+../INSTALL: installation.texi
+	rm -f ../INSTALL	
+	-$(MAKEINFO) -D INSTALLONLY \
+	  --no-validate --no-headers --no-split --output ../INSTALL $<
+
+$(DOC).html: $(DOC).texi $(CHAPTERS)
+	-$(MAKEINFO) --html --no-split $(DOC).texi
+
+$(DOC).pdf: $(DOC).texi $(CHAPTERS)
+	texi2pdf  -o $(DOC).pdf $<
+
+$(DOC).info: $(DOC).texi $(CHAPTERS)
+	-$(MAKEINFO) $<
+
+%.texi: %.txi DOCSTRINGS
+	../scripts/munge-texi -d DOCSTRINGS < $< > $@
+
+DOCSTRINGS: $(wildcard ../inst/*.m)
+	(cd ../scripts; ./mkdoc ../inst) > DOCSTRINGS || \rm -f DOCSTRINGS
+
+dist: $(DOC).pdf $(DOC).html
+	ln $(DISTFILES) ../`cat ../fname`/doc/
+
+clean:
+	\rm -f *.fns *.pdf *.aux *.log *.dvi *.out *.info *.html *.ky *.tp *.toc *.vr *.cp *.fn *.pg *.op *.au *.aus *.cps x.log *~ DOCSTRINGS $(CHAPTERS) ../INSTALL
+
+distclean: clean
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/ack.txi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,28 @@
+@c -*- texinfo -*-
+
+@c Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+@c
+@c This file is part of the queueing toolbox, a Queueing Networks
+@c analysis package for GNU Octave.
+@c
+@c The queueing toolbox is free software; you can redistribute it
+@c and/or modify it under the terms of the GNU General Public License
+@c as published by the Free Software Foundation; either version 3 of
+@c the License, or (at your option) any later version.
+@c
+@c The queueing toolbox is distributed in the hope that it will be
+@c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+@c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+@c GNU General Public License for more details.
+@c
+@c You should have received a copy of the GNU General Public License
+@c along with the queueing toolbox; see the file COPYING.  If not, see
+@c <http://www.gnu.org/licenses/>.
+
+@node Acknowledgements
+@appendix Acknowledgements
+
+The following people (listed in alphabetical order) contributed to the
+@code{queueing} package, either by providing feedback, reporting bugs
+or contributing code: Philip Carinhas, Phil Colbourn, Yves Durand,
+Marco Guazzone, Dmitry Kolesnikov.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/contributing.txi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,61 @@
+@c -*- texinfo -*-
+
+@c Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+@c
+@c This file is part of the queueing toolbox, a Queueing Networks
+@c analysis package for GNU Octave.
+@c
+@c The queueing toolbox is free software; you can redistribute it
+@c and/or modify it under the terms of the GNU General Public License
+@c as published by the Free Software Foundation; either version 3 of
+@c the License, or (at your option) any later version.
+@c
+@c The queueing toolbox is distributed in the hope that it will be
+@c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+@c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+@c GNU General Public License for more details.
+@c
+@c You should have received a copy of the GNU General Public License
+@c along with the queueing toolbox; see the file COPYING.  If not, see
+@c <http://www.gnu.org/licenses/>.
+
+@node Contributing Guidelines
+@appendix Contributing Guidelines
+
+Contributions and bug reports are @emph{always} welcome. If you want
+to contribute to the @code{queueing} package, here are some
+guidelines:
+
+@itemize
+
+@item If you are contributing a new function, please embed proper
+documentation within the function itself. The documentation must be in
+@code{texinfo} format, so that it will be extracted and formatted into
+the printable manual. See the existing functions of the
+@code{queueing} package for the documentation style.
+
+@item The documentation should be as precise as possible. In particular,
+always state what the valid ranges of the parameters are.
+
+@item If you are contributing a new function, ensure that the function
+properly checks the validity of its input parameters. For example,
+each function accepting vectors should check whether the dimensions
+match.
+
+@item Always provide bibliographic references for each algorithm you 
+contribute. If your implementation differs in some way from the
+reference you give, please describe how and why your implementation
+differs.
+
+@item Include Octave test and demo blocks with your code.
+Test blocks are particularly important, because Queueing Network
+algorithms tend to be quite complex to implement correctly, and we
+must ensure that the implementations provided with the
+@code{queueing} package are (mostly) correct.
+
+@end itemize
+
+Send your contribution to Moreno Marzolla
+(@email{marzolla@@cs.unibo.it}). Even if you are just a user of
+@code{queueing}, and find this package useful, let me know by
+dropping me a line. Thanks.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/gettingstarted.txi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,316 @@
+@c -*- texinfo -*-
+
+@c Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+@c
+@c This file is part of the queueing toolbox, a Queueing Networks
+@c analysis package for GNU Octave.
+@c
+@c The queueing toolbox is free software; you can redistribute it
+@c and/or modify it under the terms of the GNU General Public License
+@c as published by the Free Software Foundation; either version 3 of
+@c the License, or (at your option) any later version.
+@c
+@c The queueing toolbox is distributed in the hope that it will be
+@c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+@c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+@c GNU General Public License for more details.
+@c
+@c You should have received a copy of the GNU General Public License
+@c along with the queueing toolbox; see the file COPYING.  If not, see
+@c <http://www.gnu.org/licenses/>.
+
+@node Getting Started
+@chapter Introduction and Getting Started
+
+@menu
+* Analysis of Closed Networks::
+* Analysis of Open Networks::
+@end menu
+
+In this chapter we give some usage examples of the @code{queueing}
+package. The reader is assumed to be familiar with Queueing Networks
+(although some basic terminology and notation will be given
+here). Additional usage examples are embedded in most of the function
+files; to display and execute the demos associated with function
+@emph{fname} you can type @command{demo @emph{fname}} at the Octave
+prompt. For example
+
+@example
+@kbd{demo qnclosed}
+@end example
+
+@noindent executes all demos (if any) for the @command{qnclosed} function.
+
+@node Analysis of Closed Networks
+@section Analysis of Closed Networks
+
+Let us consider a simple closed network with @math{K=3} service
+centers. Each center is of type @math{M/M/1}--FCFS. We denote with
+@math{S_i} the average service time at center @math{i}, @math{i=1, 2,
+3}. Let @math{S_1 = 1.0}, @math{S_2 = 2.0} and @math{S_3 = 0.8}. The
+routing of jobs within the network is described with a @emph{routing
+probability matrix} @math{P}. Specifically, a request completing
+service at center @math{i} is enqueued at center @math{j} with
+probability @math{P_{ij}}.  Let us assume the following routing
+probability matrix:
+
+@iftex
+@tex
+$$
+P = \pmatrix{ 0 & 0.3 & 0.7 \cr
+              1 & 0 & 0 \cr
+              1 & 0 & 0 }
+$$
+@end tex
+@end iftex
+@ifnottex
+@example
+    [ 0  0.3  0.7 ] 
+P = [ 1  0    0   ]
+    [ 1  0    0   ]
+@end example
+@end ifnottex
+
+For example, according to matric @math{P} a job completing service at
+center 1 is routed to center 2 with probability 0.3, and is routed to
+center 3 with probability 0.7.
+
+The network above can be analyzed with the @command{qnclosed}
+function; if there is just a single class of requests, as in the
+example above, @command{qnclosed} calls @command{qnclosedsinglemva}
+which implements the Mean Value Analysys (MVA) algorithm for
+single-class, product-form network.
+
+@command{qnclosed} requires the following parameters:
+
+@table @var
+
+@item N
+Number of requests in the network (since we are considering a closed
+network, the number of requests is fixed)
+
+@item S
+Array of average service times at the centers: @code{@var{S}(k)} is
+the average service time at center @math{k}.
+
+@item V
+Array of visit ratios: @code{@var{V}(k)} is the average number of
+visits to center @math{k}.
+
+@end table
+
+As can be seen, we must compute the @emph{visit ratios} (or visit
+counts) @math{V_k} for each center @math{k}. The visit counts satisfy
+the following equations:
+
+@iftex
+@tex
+$$
+V_j = \sum_{i=1}^K V_i P_{ij}
+$$
+@end tex
+@end iftex
+@ifnottex
+@example     
+V_j = sum_i V_i P_ij
+@end example
+@end ifnottex
+
+We can compute @math{V_k} from the routing probability matrix
+@math{P_{ij}} using the @command{qnvisits} function:
+
+@example
+@group
+@kbd{P = [0 0.3 0.7; 1 0 0; 1 0 0];}
+@kbd{V = qnvisits(P)}
+   @result{} V = 1.00000 0.30000 0.70000
+@end group
+@end example
+
+We can check that the computed values satisfy the above equation by
+evaluating the following expression:
+
+@example
+@kbd{V*P}
+     @result{} ans = 1.00000 0.30000 0.70000
+@end example
+
+@noindent which is equal to @math{V}.
+Hence, we can analyze the network for a given population size @math{N}
+(for example, @math{N=10}) as follows:
+
+@example
+@group
+@kbd{N = 10;}
+@kbd{S = [1 2 0.8];}
+@kbd{P = [0 0.3 0.7; 1 0 0; 1 0 0];}
+@kbd{V = qnvisits(P);}
+@kbd{[U R Q X] = qnclosed( N, S, V )}
+   @result{} U = 0.99139 0.59483 0.55518 
+   @result{} R = 7.4360  4.7531  1.7500 
+   @result{} Q = 7.3719  1.4136  1.2144 
+   @result{} X = 0.99139 0.29742 0.69397 
+@end group
+@end example
+
+The output of @command{qnclosed} includes the vector of utilizations
+@math{U_k} at center @math{k}, response time @math{R_k}, average
+number of customers @math{Q_k} and throughput @math{X_k}. In our
+example, the throughput of center 1 is @math{X_1 = 0.99139}, and the
+average number of requests in center 3 is @math{Q_3 = 1.2144}. The
+utilization of center 1 is @math{U_1 = 0.99139}, which is the higher
+value among the service centers. Tus, center 1 is the @emph{bottleneck
+device}.
+
+This network can also be analyzed with the @command{qnsolve}
+function. @command{qnsolve} can handle open, closed or mixed networks,
+and allows the network to be described in a very flexible way.  First,
+let @var{Q1}, @var{Q2} and @var{Q3} be the variables describing the
+service centers. Each variable is instantiated with the
+@command{qnmknode} function.
+
+@example
+@group
+@kbd{Q1 = qnmknode( "m/m/m-fcfs", 1 );}
+@kbd{Q2 = qnmknode( "m/m/m-fcfs", 2 );}
+@kbd{Q3 = qnmknode( "m/m/m-fcfs", 0.8 );}
+@end group
+@end example
+
+The first parameter of @command{qnmknode} is a string describing the
+type of the node. Here we use @code{"m/m/m-fcfs"} to denote a
+@math{M/M/m}--FCFS center. The second parameter gives the average
+service time. An optional third parameter can be used to specify the
+number @math{m} of service centers. If omitted, it is assumed
+@math{m=1} (single-server node).
+
+Now, the network can be analyzed as follows:
+
+@example
+@group
+@kbd{N = 10;}
+@kbd{V = [1 0.3 0.7];}
+@kbd{[U R Q X] = qnsolve( "closed", N, @{ Q1, Q2, Q3 @}, V )}
+   @result{} U = 0.99139 0.59483 0.55518 
+   @result{} R = 7.4360  4.7531  1.7500 
+   @result{} Q = 7.3719  1.4136  1.2144 
+   @result{} X = 0.99139 0.29742 0.69397 
+@end group
+@end example
+
+Of course, we get exactly the same results. Other functions can be used
+for closed networks, @pxref{Algorithms for Product-Form QNs}.
+
+@node Analysis of Open Networks
+@section Analysis of Open Networks
+
+Open networks can be analyzed in a similar way. Let us consider
+an open network with @math{K=3} service centers, and routing
+probability matrix as follows:
+
+@iftex
+@tex
+$$
+P = \pmatrix{ 0 & 0.3 & 0.5 \cr
+              1 & 0 & 0 \cr
+              1 & 0 & 0 }
+$$
+@end tex
+@end iftex
+@ifnottex
+@example
+    [ 0  0.3  0.5 ]
+P = [ 1  0    0   ]
+    [ 1  0    0   ]
+@end example
+@end ifnottex
+
+In this network, requests can leave the system from center 1 with
+probability @math{(1-(0.3+0.5) = 0.2}. We suppose that external jobs
+arrive at center 1 with rate @math{\lambda_1 = 0.15}; there are no
+arrivals at centers 2 and 3.
+
+Similarly to closed networks, we first need to compute the visit
+counts @math{V_k} to center @math{k}. Again, we use the
+@command{qnvisits} function as follows:
+
+@example
+@group
+@kbd{P = [0 0.3 0.5; 1 0 0; 1 0 0];}
+@kbd{lambda = [0.15 0 0];}
+@kbd{V = qnvisits(P, lambda)}
+   @result{} V = 5.00000 1.50000 2.50000
+@end group
+@end example
+
+@noindent where @code{@var{lambda}(k)} is the arrival rate at center @math{k},
+and @var{P} is the routing matrix. The visit counts @math{V_k} for
+open networks satisfy the following equation:
+
+@iftex
+@tex
+$$
+V_j = P_{0j} + \sum_{i=1}^K V_i P_{ij}
+$$
+@end tex
+@end iftex
+@ifnottex
+@example
+V_j = sum_i V_i P_ij
+@end example
+@end ifnottex
+
+where @math{P_{0j}} is the probability of an external arrival to
+center @math{j}. This can be computed as:
+
+@tex
+$$
+P_{0j} = {\lambda_j  \over \sum_{i=1}^K \lambda_i }
+$$
+@end tex
+
+Assuming the same service times as in the previous example, the
+network can be analyzed with the @command{qnopen} function, as
+follows:
+
+@example
+@group
+@kbd{S = [1 2 0.8];}
+@kbd{[U R Q X] = qnopen( sum(lambda), S, V )}
+   @result{} U = 0.75000 0.45000 0.30000 
+   @result{} R = 4.0000  3.6364  1.1429
+   @result{} Q = 3.00000 0.81818 0.42857
+   @result{} X = 0.75000 0.22500 0.37500 
+@end group
+@end example
+
+The first parameter of the @command{qnopen} function is the (scalar)
+aggregate arrival rate.
+
+Again, it is possible to use the @command{qnsolve} high-level function:
+
+@example
+@group
+@kbd{Q1 = qnmknode( "m/m/m-fcfs", 1 );}
+@kbd{Q2 = qnmknode( "m/m/m-fcfs", 2 );}
+@kbd{Q3 = qnmknode( "m/m/m-fcfs", 0.8 );}
+@kbd{lambda = [0.15 0 0];}
+@kbd{[U R Q X] = qnsolve( "open", sum(lambda), @{ Q1, Q2, Q3 @}, V )}
+   @result{} U = 0.75000 0.45000 0.30000 
+   @result{} R = 4.0000  3.6364  1.1429
+   @result{} Q = 3.00000 0.81818 0.42857
+   @result{} X = 0.75000 0.22500 0.37500 
+@end group
+@end example
+
+@c @node Markov Chains Analysis
+@c @section Markov Chains Analysis
+
+@c @subsection Discrete-Time Markov Chains
+
+@c (TODO)
+
+@c @subsection Continuous-Time Markov Chains
+
+@c (TODO)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/gpl.txi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,718 @@
+@node Copying
+@appendix GNU GENERAL PUBLIC LICENSE
+@cindex warranty
+@cindex copyright
+
+@center Version 3, 29 June 2007
+
+@display
+Copyright @copyright{} 2007 Free Software Foundation, Inc. @url{http://fsf.org/}
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+@end display
+
+@heading Preamble
+
+The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom
+to share and change all versions of a program---to make sure it remains
+free software for all its users.  We, the Free Software Foundation,
+use the GNU General Public License for most of our software; it
+applies also to any other work released this way by its authors.  You
+can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you
+have certain responsibilities if you distribute copies of the
+software, or if you modify it: responsibilities to respect the freedom
+of others.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too,
+receive or can get the source code.  And you must show them these
+terms so they know their rights.
+
+Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the
+manufacturer can do so.  This is fundamentally incompatible with the
+aim of protecting users' freedom to change the software.  The
+systematic pattern of such abuse occurs in the area of products for
+individuals to use, which is precisely where it is most unacceptable.
+Therefore, we have designed this version of the GPL to prohibit the
+practice for those products.  If such problems arise substantially in
+other domains, we stand ready to extend this provision to those
+domains in future versions of the GPL, as needed to protect the
+freedom of users.
+
+Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish
+to avoid the special danger that patents applied to a free program
+could make it effectively proprietary.  To prevent this, the GPL
+assures that patents cannot be used to render the program non-free.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+@heading TERMS AND CONDITIONS
+
+@enumerate 0
+@item Definitions.
+
+``This License'' refers to version 3 of the GNU General Public License.
+
+``Copyright'' also means copyright-like laws that apply to other kinds
+of works, such as semiconductor masks.
+
+``The Program'' refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as ``you''.  ``Licensees'' and
+``recipients'' may be individuals or organizations.
+
+To ``modify'' a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of
+an exact copy.  The resulting work is called a ``modified version'' of
+the earlier work or a work ``based on'' the earlier work.
+
+A ``covered work'' means either the unmodified Program or a work based
+on the Program.
+
+To ``propagate'' a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+To ``convey'' a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user
+through a computer network, with no transfer of a copy, is not
+conveying.
+
+An interactive user interface displays ``Appropriate Legal Notices'' to
+the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+@item Source Code.
+
+The ``source code'' for a work means the preferred form of the work for
+making modifications to it.  ``Object code'' means any non-source form
+of a work.
+
+A ``Standard Interface'' means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+The ``System Libraries'' of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+``Major Component'', in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+The ``Corresponding Source'' for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+The Corresponding Source need not include anything that users can
+regenerate automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same
+work.
+
+@item Basic Permissions.
+
+All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+You may make, run and propagate covered works that you do not convey,
+without conditions so long as your license otherwise remains in force.
+You may convey covered works to others for the sole purpose of having
+them make modifications exclusively for you, or provide you with
+facilities for running those works, provided that you comply with the
+terms of this License in conveying all material for which you do not
+control copyright.  Those thus making or running the covered works for
+you must do so exclusively on your behalf, under your direction and
+control, on terms that prohibit them from making any copies of your
+copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the
+conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+@item Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such
+circumvention is effected by exercising rights under this License with
+respect to the covered work, and you disclaim any intention to limit
+operation or modification of the work as a means of enforcing, against
+the work's users, your or third parties' legal rights to forbid
+circumvention of technological measures.
+
+@item Conveying Verbatim Copies.
+
+You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+@item Conveying Modified Source Versions.
+
+You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these
+conditions:
+
+@enumerate a
+@item 
+The work must carry prominent notices stating that you modified it,
+and giving a relevant date.
+
+@item
+The work must carry prominent notices stating that it is released
+under this License and any conditions added under section 7.  This
+requirement modifies the requirement in section 4 to ``keep intact all
+notices''.
+
+@item
+You must license the entire work, as a whole, under this License to
+anyone who comes into possession of a copy.  This License will
+therefore apply, along with any applicable section 7 additional terms,
+to the whole of the work, and all its parts, regardless of how they
+are packaged.  This License gives no permission to license the work in
+any other way, but it does not invalidate such permission if you have
+separately received it.
+
+@item
+If the work has interactive user interfaces, each must display
+Appropriate Legal Notices; however, if the Program has interactive
+interfaces that do not display Appropriate Legal Notices, your work
+need not make them do so.
+@end enumerate
+
+A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+``aggregate'' if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+@item  Conveying Non-Source Forms.
+
+You may convey a covered work in object code form under the terms of
+sections 4 and 5, provided that you also convey the machine-readable
+Corresponding Source under the terms of this License, in one of these
+ways:
+
+@enumerate a
+@item
+Convey the object code in, or embodied in, a physical product
+(including a physical distribution medium), accompanied by the
+Corresponding Source fixed on a durable physical medium customarily
+used for software interchange.
+
+@item
+Convey the object code in, or embodied in, a physical product
+(including a physical distribution medium), accompanied by a written
+offer, valid for at least three years and valid for as long as you
+offer spare parts or customer support for that product model, to give
+anyone who possesses the object code either (1) a copy of the
+Corresponding Source for all the software in the product that is
+covered by this License, on a durable physical medium customarily used
+for software interchange, for a price no more than your reasonable
+cost of physically performing this conveying of source, or (2) access
+to copy the Corresponding Source from a network server at no charge.
+
+@item
+Convey individual copies of the object code with a copy of the written
+offer to provide the Corresponding Source.  This alternative is
+allowed only occasionally and noncommercially, and only if you
+received the object code with such an offer, in accord with subsection
+6b.
+
+@item
+Convey the object code by offering access from a designated place
+(gratis or for a charge), and offer equivalent access to the
+Corresponding Source in the same way through the same place at no
+further charge.  You need not require recipients to copy the
+Corresponding Source along with the object code.  If the place to copy
+the object code is a network server, the Corresponding Source may be
+on a different server (operated by you or a third party) that supports
+equivalent copying facilities, provided you maintain clear directions
+next to the object code saying where to find the Corresponding Source.
+Regardless of what server hosts the Corresponding Source, you remain
+obligated to ensure that it is available for as long as needed to
+satisfy these requirements.
+
+@item
+Convey the object code using peer-to-peer transmission, provided you
+inform other peers where the object code and Corresponding Source of
+the work are being offered to the general public at no charge under
+subsection 6d.
+
+@end enumerate
+
+A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+A ``User Product'' is either (1) a ``consumer product'', which means any
+tangible personal property which is normally used for personal,
+family, or household purposes, or (2) anything designed or sold for
+incorporation into a dwelling.  In determining whether a product is a
+consumer product, doubtful cases shall be resolved in favor of
+coverage.  For a particular product received by a particular user,
+``normally used'' refers to a typical or common use of that class of
+product, regardless of the status of the particular user or of the way
+in which the particular user actually uses, or expects or is expected
+to use, the product.  A product is a consumer product regardless of
+whether the product has substantial commercial, industrial or
+non-consumer uses, unless such uses represent the only significant
+mode of use of the product.
+
+``Installation Information'' for a User Product means any methods,
+procedures, authorization keys, or other information required to
+install and execute modified versions of a covered work in that User
+Product from a modified version of its Corresponding Source.  The
+information must suffice to ensure that the continued functioning of
+the modified object code is in no case prevented or interfered with
+solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or
+updates for a work that has been modified or installed by the
+recipient, or for the User Product in which it has been modified or
+installed.  Access to a network may be denied when the modification
+itself materially and adversely affects the operation of the network
+or violates the rules and protocols for communication across the
+network.
+
+Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+@item Additional Terms.
+
+``Additional permissions'' are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders
+of that material) supplement the terms of this License with terms:
+
+@enumerate a
+@item
+Disclaiming warranty or limiting liability differently from the terms
+of sections 15 and 16 of this License; or
+
+@item
+Requiring preservation of specified reasonable legal notices or author
+attributions in that material or in the Appropriate Legal Notices
+displayed by works containing it; or
+
+@item
+Prohibiting misrepresentation of the origin of that material, or
+requiring that modified versions of such material be marked in
+reasonable ways as different from the original version; or
+
+@item
+Limiting the use for publicity purposes of names of licensors or
+authors of the material; or
+
+@item
+Declining to grant rights under trademark law for use of some trade
+names, trademarks, or service marks; or
+
+@item
+Requiring indemnification of licensors and authors of that material by
+anyone who conveys the material (or modified versions of it) with
+contractual assumptions of liability to the recipient, for any
+liability that these contractual assumptions directly impose on those
+licensors and authors.
+@end enumerate
+
+All other non-permissive additional terms are considered ``further
+restrictions'' within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions; the
+above requirements apply either way.
+
+@item Termination.
+
+You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+@item Acceptance Not Required for Having Copies.
+
+You are not required to accept this License in order to receive or run
+a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+@item Automatic Licensing of Downstream Recipients.
+
+Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+An ``entity transaction'' is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+@item Patents.
+
+A ``contributor'' is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's ``contributor version''.
+
+A contributor's ``essential patent claims'' are all patent claims owned
+or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, ``control'' includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+In the following three paragraphs, a ``patent license'' is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To ``grant'' such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  ``Knowingly relying'' means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+A patent license is ``discriminatory'' if it does not include within the
+scope of its coverage, prohibits the exercise of, or is conditioned on
+the non-exercise of one or more of the rights that are specifically
+granted under this License.  You may not convey a covered work if you
+are a party to an arrangement with a third party that is in the
+business of distributing software, under which you make payment to the
+third party based on the extent of your activity of conveying the
+work, and under which the third party grants, to any of the parties
+who would receive the covered work from you, a discriminatory patent
+license (a) in connection with copies of the covered work conveyed by
+you (or copies made from those copies), or (b) primarily for and in
+connection with specific products or compilations that contain the
+covered work, unless you entered into that arrangement, or that patent
+license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+@item No Surrender of Others' Freedom.
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey
+a covered work so as to satisfy simultaneously your obligations under
+this License and any other pertinent obligations, then as a
+consequence you may not convey it at all.  For example, if you agree
+to terms that obligate you to collect a royalty for further conveying
+from those to whom you convey the Program, the only way you could
+satisfy both those terms and this License would be to refrain entirely
+from conveying the Program.
+
+@item Use with the GNU Affero General Public License.
+
+Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+@item Revised Versions of this License.
+
+The Free Software Foundation may publish revised and/or new versions
+of the GNU General Public License from time to time.  Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies that a certain numbered version of the GNU General Public
+License ``or any later version'' applies to it, you have the option of
+following the terms and conditions either of that numbered version or
+of any later version published by the Free Software Foundation.  If
+the Program does not specify a version number of the GNU General
+Public License, you may choose any version ever published by the Free
+Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions
+of the GNU General Public License can be used, that proxy's public
+statement of acceptance of a version permanently authorizes you to
+choose that version for the Program.
+
+Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+@item Disclaimer of Warranty.
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM ``AS IS'' WITHOUT
+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+@item Limitation of Liability.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
+CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
+NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
+LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
+TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
+PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+@item Interpretation of Sections 15 and 16.
+
+If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+@end enumerate
+
+@heading END OF TERMS AND CONDITIONS
+
+@heading How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the ``copyright'' line and a pointer to where the full notice is found.
+
+@smallexample
+@var{one line to give the program's name and a brief idea of what it does.}  
+Copyright (C) @var{year} @var{name of author}
+
+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 3 of the License, or (at
+your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see @url{http://www.gnu.org/licenses/}.
+@end smallexample
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+@smallexample
+@var{program} Copyright (C) @var{year} @var{name of author} 
+This program comes with ABSOLUTELY NO WARRANTY; for details type @samp{show w}.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type @samp{show c} for details.
+@end smallexample
+
+The hypothetical commands @samp{show w} and @samp{show c} should show
+the appropriate parts of the General Public License.  Of course, your
+program's commands might be different; for a GUI interface, you would
+use an ``about box''.
+
+You should also get your employer (if you work as a programmer) or school,
+if any, to sign a ``copyright disclaimer'' for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+@url{http://www.gnu.org/licenses/}.
+
+The GNU General Public License does not permit incorporating your
+program into proprietary programs.  If your program is a subroutine
+library, you may consider it more useful to permit linking proprietary
+applications with the library.  If this is what you want to do, use
+the GNU Lesser General Public License instead of this License.  But
+first, please read @url{http://www.gnu.org/philosophy/why-not-lgpl.html}.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/installation.txi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,245 @@
+@c -*- texinfo -*-
+
+@c Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+@c
+@c This file is part of the queueing toolbox, a Queueing Networks
+@c analysis package for GNU Octave.
+@c
+@c The queueing toolbox is free software; you can redistribute it
+@c and/or modify it under the terms of the GNU General Public License
+@c as published by the Free Software Foundation; either version 3 of
+@c the License, or (at your option) any later version.
+@c
+@c The queueing toolbox is distributed in the hope that it will be
+@c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+@c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+@c GNU General Public License for more details.
+@c
+@c You should have received a copy of the GNU General Public License
+@c along with the queueing toolbox; see the file COPYING.  If not, see
+@c <http://www.gnu.org/licenses/>.
+
+@ifset INSTALLONLY
+@include conf.texi
+
+This file documents the installation procedure of the @code{queueing}
+toolbox.
+
+@code{queueing} is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License, version 3
+or later, as published by the Free Software Foundation.
+
+@quotation Note
+This file (@file{INSTALL}) is automatically generated from
+@file{doc/installation.txi} in the @code{queueing} sources.  Do not
+modify this document directly, as changes will be lost. Modify the
+source @file{doc/installation.txi} instead.
+@end quotation
+
+@end ifset
+
+@node Installation
+@chapter Installing the queueing toolbox
+
+@menu
+* Installation through Octave package management system::
+* Manual installation::
+* Content of the source distribution::
+* Using the queueing toolbox::
+@end menu
+
+@c
+@c
+@c
+
+@node Installation through Octave package management system
+@section Installation through Octave package management system
+
+The most recent version of @code{queueing} is @value{VERSION} and can
+be downloaded from
+
+@url{http://www.moreno.marzolla.name/software/queueing/queueing-@value{VERSION}.tar.gz}
+
+To install @code{queueing} in the system-wide location, such that all
+functions are automatically available when Octave starts, you can use
+@samp{pkg install} command. At the Octave prompt, type the following:
+
+@example
+octave:1> @kbd{pkg install queueing-@value{VERSION}.tar.gz}
+@end example
+
+(Note: you may need to start Octave as root in order to allow the
+installation to copy the files to the target locations). After this,
+all functions will be readily available each time Octave starts,
+without the need to tweak the search path. To uninstall
+@code{queueing}, use the @samp{pkg uninstall queueing} command.
+
+If you do not have root access, you can do a local installation by
+issuing the following command at the Octave prompt:
+
+@example
+octave:1> @kbd{pkg install -local queueing-@value{VERSION}.tar.gz}
+@end example
+
+This will install @code{queueing} within the user's home directory,
+and the package will be available to that user only. @strong{Note:}
+Octave version 3.2.3 as shipped with Ubuntu 10.04 seems to ignore
+@code{-local} and always tries to install the package on the system
+directory.
+
+@c
+@c
+@c
+
+@node Manual installation
+@section Manual installation
+
+If you want to install @code{queueing} in a custom location, you can
+download the source tarball from the URL above, and unpack it
+somewhere:
+
+@example
+@kbd{tar xfz queueing-@value{VERSION}.tar.gz}
+@kbd{cd queueing-@value{VERSION}/}
+@end example
+
+Copy all @code{.m} files from the @file{inst/} directory to some
+target location. Then, you can start Octave with the @option{-p}
+option to add the target location to the search path, so that Octave
+will find all @code{queueing} functions automatically:
+
+@example
+@kbd{octave -p @emph{/path/to/queueing}}
+@end example
+
+For example, if all @code{queueing} m-files are in
+@file{/usr/local/queueing}, you can start Octave as follows:
+
+@example
+@kbd{octave -p @file{/usr/local/queueing}}
+@end example
+
+If you want, you can add the following line to @file{~/.octaverc}:
+
+@example
+@kbd{addpath("@emph{/path/to/queueing}");}
+@end example
+
+@noindent so that the path @file{/usr/local/queueing} is automatically
+added to the search path each time Octave is started, and you no
+longer need to specify the @option{-p} option on the command line.
+
+@c
+@c
+@c
+
+@node Content of the source distribution
+@section Content of the source distribution
+
+The @code{queueing} source distribution contains the following
+subdirectories:
+
+@table @file
+@item doc/
+Documentation source. Most of the documentation is extracted from the
+comment blocks of individual function files from the @file{inst/}
+directory.
+
+@item inst/
+This directory contains the @verb{|m|}-files which implement the
+various Queueing Network algorithms provided by @code{queueing}. As a
+notational convention, the names of source files containing functions
+for Queueing Networks start with the @samp{qn} prefix; the name of
+source files containing functions for Continuous-Time Markov Chains
+(CTMSs) start with the @samp{ctmc} prefix, and the names of files
+containing functions for Discrete-Time Markov Chains (DTMCs) start
+with the @samp{dtmc} prefix.
+
+@item test/
+This directory contains the test functions used to invoke all tests on
+all function files.
+
+@item scripts/
+This directory contains some utility scripts mostly from GNU Octave,
+which extract the documentation from the specially-formatted comments
+in the @verb{|m|}-files.
+
+@item examples/
+This directory contains examples which are automatically extracted
+from the @samp{demo} blocks of the function files.
+
+@item broken/
+This directory contains function files which are either not working
+properly, or need additional testing before they can be moved to the
+@file{inst/} directory.
+
+@end table
+
+The @code{queueing} package ships with a Makefile which can be used
+to produce the documentation (in PDF and HTML format), and
+automatically execute all function tests. Specifically, the following
+targets are defined:
+
+@table @code
+@item all
+Running @samp{make} (or @samp{make all}) on the top-level directory
+builds the programs used to extract the documentation from the
+comments embedded in the @verb{|m|}-files, and then produce the
+documentation in PDF and HTML format (@file{doc/queueing.pdf} and
+@file{doc/queueing.html}, respectively).
+
+@item check
+Running @samp{make check} will execute all tests contained in the
+@verb{|m|}-files. If you modify the code of any function in the
+@file{inst/} directory, you should run the tests to ensure that no
+errors have been introduced. You are also encouraged to contribute new
+tests, especially for functions which are not adequately validated.
+
+@item clean
+@itemx distclean
+@itemx dist
+The @samp{make clean}, @samp{make distclean} and @samp{make dist}
+commands are used to clean up the source directory and prepare the
+distribution archive in compressed tar format.
+
+@end table
+
+@c
+@c
+@c
+
+@node Using the queueing toolbox
+@section Using the queueing toolbox
+
+You can use all functions by simply invoking their name with the
+appropriate parameters; the @code{queueing} package should display an
+error message in case of missing/wrong parameters. You can display the
+help text for any function using the @command{help} command. For
+example:
+
+@example
+octave:2> @kbd{help qnmvablo}
+@end example
+
+prints the documentation for the @command{qnmvablo} function.
+Additional information can be found in the @code{queueing} manual,
+which is available in PDF format in @file{doc/queueing.pdf} and in
+HTML format in @file{doc/queueing.html}.
+
+Within GNU Octave, you can also run the test and demo blocks
+associated to the functions, using the @command{test} and
+@command{demo} commands respectively. To run all the tests of, say,
+the @command{qnmvablo} function:
+
+@example
+octave:3> @kbd{test qnmvablo}
+@print{} PASSES 4 out of 4 tests
+@end example
+
+To execute the demos of the @command{qnclosed} function, use the
+following:
+
+@example
+octave:4> @kbd{demo qnclosed}
+@end example
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/markovchains.txi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,253 @@
+@c -*- texinfo -*-
+
+@c Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+@c
+@c This file is part of the queueing toolbox, a Queueing Networks
+@c analysis package for GNU Octave.
+@c
+@c The queueing toolbox is free software; you can redistribute it
+@c and/or modify it under the terms of the GNU General Public License
+@c as published by the Free Software Foundation; either version 3 of
+@c the License, or (at your option) any later version.
+@c
+@c The queueing toolbox is distributed in the hope that it will be
+@c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+@c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+@c GNU General Public License for more details.
+@c
+@c You should have received a copy of the GNU General Public License
+@c along with the queueing toolbox; see the file COPYING.  If not, see
+@c <http://www.gnu.org/licenses/>.
+
+@node Markov Chains
+@chapter Markov Chains
+
+@menu
+* Discrete-Time Markov Chains::
+* Continuous-Time Markov Chains::
+@end menu
+
+@node Discrete-Time Markov Chains
+@section Discrete-Time Markov Chains
+
+@menu
+* DTMC Stationary Probability::
+* DTMC First Passage Times::
+@end menu
+
+@node DTMC Stationary Probability
+@subsection Stationary Probability
+
+@DOCSTRING(dtmc)
+
+@node DTMC First Passage Times
+@subsection First Passage Times
+
+The First Passage Time @math{M_{i j}} is defined as the average
+number of transitions needed to visit state @math{j} for the first
+time, starting from state @math{i}. Matrix @math{\bf M} satisfies the
+property that
+
+@iftex
+@tex
+$$ M_{i j} = 1 + \sum_{k \neq j} P_{i k} M_{k j}$$
+@end tex
+@end iftex
+@ifnottex
+@example
+@group
+           ___
+          \
+M_ij = 1 + >   P_ij * M_kj
+          /___ 
+          k!=j 
+@end group
+@end example
+@end ifnottex
+
+@DOCSTRING(dtmc_fpt)
+
+@c
+@c
+@c
+@node Continuous-Time Markov Chains
+@section Continuous-Time Markov Chains
+
+@menu
+* CTMC Stationary Probability::
+* Birth-Death process::
+* Expected Sojourn Time::
+* Time-Averaged Expected Sojourn Time::
+* Expected Time to Absorption::
+* CTMC First Passage Times::
+@end menu
+
+@node CTMC Stationary Probability
+@subsection Stationary Probability
+
+@DOCSTRING(ctmc)
+
+@noindent @strong{EXAMPLE}
+
+Consider a two-state CTMC such that transition rates between states
+are equal to 1. This can be solved as follows:
+
+@example
+@group
+@verbatiminclude @value{top_srcdir}/examples/demo_1_ctmc.m
+    @result{} q = 0.50000   0.50000
+@end group
+@end example
+
+@c
+@c
+@c
+@node Birth-Death process
+@subsection Birth-Death process
+
+@DOCSTRING(ctmc_bd)
+
+@c
+@c
+@c
+@node Expected Sojourn Time
+@subsection Expected Sojourn Time
+
+Given a @math{N} state continuous-time Markov Chain with infinitesimal
+generator matrix @math{\bf Q}, we define the vector @math{{\bf L}(t) =
+(L_1(t), L_2(t), \ldots L_N(t))} such that @math{L_i(t)} is the
+expected sojourn time in state @math{i} during the interval
+@math{[0,t)}, assuming that the initial occupancy probability at time
+0 was @math{{\bf \pi}(0)}. Then, @math{{\bf L}(t)} is the solution of
+the following differential equation:
+
+@iftex
+@tex
+$$ { d{\bf L}(t) \over dt} = {\bf L}(t){\bf Q} + {\bf \pi}(0), \qquad {\bf L}(0) = {\bf 0} $$
+@end tex
+@end iftex
+@ifnottex
+@example
+@group
+ dL
+ --(t) = L(t) Q + pi(0),    L(0) = 0
+ dt
+@end group
+@end example
+@end ifnottex
+
+The function @code{ctmc_exps} can be used to compute @math{{\bf
+L}(t)}, by using the @code{lsode} Octave function to solve the above
+linear differential equation.
+
+@DOCSTRING(ctmc_exps)
+
+@noindent @strong{EXAMPLE}
+
+Let us consider a pure-birth, 4-states CTMC such that the transition
+rate from state @math{i} to state @math{i+1} is @math{\lambda_i = i
+\lambda} (@math{i=1, 2, 3}), with @math{\lambda = 0.5}. The following
+code computes the expected sojourn time in state @math{i},
+given the initial occupancy probability @math{p_0=(1,0,0,0)}. 
+
+@example
+@group
+@verbatiminclude @value{top_srcdir}/examples/demo_1_ctmc_exps.m
+@end group
+@end example
+
+@c
+@c
+@c
+@node Time-Averaged Expected Sojourn Time
+@subsection Time-Averaged Expected Sojourn Time
+
+@DOCSTRING(ctmc_taexps)
+
+@noindent @strong{EXAMPLE}
+
+@example
+@group
+@verbatiminclude @value{top_srcdir}/examples/demo_1_ctmc_taexps.m
+@end group
+@end example
+
+@c
+@c
+@c
+@node Expected Time to Absorption
+@subsection Expected Time to Absorption
+
+If we consider a Markov Chain with absorbing states, it is possible to
+define the @emph{expected time to absorption} as the expected time
+until the system goes into an absorbing state. More specifically, let
+us suppose that @math{A} is the set of transient (i.e., non-absorbing)
+states of a CTMC with @math{N} states and infinitesimal generator
+matrix @math{\bf Q}. The expected time to absorption @math{{\bf
+L}_A(\infty)} is defined as the solution of the following equation:
+
+@iftex
+@tex
+$$ {\bf L}_A(\infty){\bf Q}_A = -{\bf \pi}_A(0) $$
+@end tex
+@end iftex
+@ifnottex
+@example
+@group
+L_A( inf ) Q_A = -pi_A(0)
+@end group
+@end example
+@end ifnottex
+
+@noindent where @math{{\bf Q}_A} is the restriction of matrix @math{\bf Q} to
+only states in @math{A}, and @math{{\bf \pi}_A(0)} is the initial
+state occupancy probability at time 0, restricted to states in
+@math{A}.
+
+@DOCSTRING(ctmc_mtta)
+
+@noindent @strong{EXAMPLE}
+
+Let us consider a simple model of a redundant disk array. We assume
+that the array is made of 5 independent disks, such that the array can
+tolerate up to 2 disk failures without losing data. If three or more
+disks break, the array is dead and unrecoverable. We want to estimate
+the Mean-Time-To-Failure (MTTF) of the disk array.
+
+We model this system as a 4 states Markov chain with state space
+@math{\{ 2, 3, 4, 5 \}}. State @math{i} denotes the fact that exactly
+@math{i} disks are active; state @math{2} is absorbing. Let @math{\mu}
+be the failure rate of a single disk. The system starts in state
+@math{5} (all disks are operational). We use a pure death process,
+with death rate from state @math{i} to state @math{i-1} is @math{\mu
+i}, for @math{i = 3, 4, 5}).
+
+The MTTF of the disk array is the MTTA of the Markov Chain, and can be
+computed with the following expression:
+
+@example
+@group
+@verbatiminclude @value{top_srcdir}/examples/demo_1_ctmc_mtta.m
+    @result{} t = 78.333
+@end group
+@end example
+
+@noindent @strong{REFERENCES}
+
+G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, @cite{Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications}, Wiley,
+1998.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@c
+@c
+@c
+@node CTMC First Passage Times
+@subsection First Passage Times
+@DOCSTRING(ctmc_fpt)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/queueing.html	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,5482 @@
+<html lang="en">
+<head>
+<title>queueing</title>
+<meta http-equiv="Content-Type" content="text/html">
+<meta name="description" content="User manual for the queueing toolbox, a GNU Octave package for queueing networks and Markov chains analysis. This package supports single-station queueing systems, queueing networks and Markov chains. The queueing toolbox implements, among others, the Mean Value Analysis (MVA) and convolution algorithms for product-form queueing networks. Transient and steady-state analysis of Markov chains is also implemented.">
+<meta name="generator" content="makeinfo 4.13">
+<link title="Top" rel="top" href="#Top">
+<link href="http://www.gnu.org/software/texinfo/" rel="generator-home" title="Texinfo Homepage">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<style type="text/css"><!--
+  pre.display { font-family:inherit }
+  pre.format  { font-family:inherit }
+  pre.smalldisplay { font-family:inherit; font-size:smaller }
+  pre.smallformat  { font-family:inherit; font-size:smaller }
+  pre.smallexample { font-size:smaller }
+  pre.smalllisp    { font-size:smaller }
+  span.sc    { font-variant:small-caps }
+  span.roman { font-family:serif; font-weight:normal; } 
+  span.sansserif { font-family:sans-serif; font-weight:normal; } 
+--></style>
+</head>
+<body>
+Copyright &copy; 2008, 2009, 2010, 2011, 2012 Moreno Marzolla.
+
+   <p>Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+   <p>Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided that
+the entire resulting derived work is distributed under the terms of
+a permission notice identical to this one.
+
+   <p>Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for
+modified versions.
+
+<div class="contents">
+<h2>Table of Contents</h2>
+<ul>
+<li><a name="toc_Top" href="#Top">queueing</a>
+<li><a name="toc_Summary" href="#Summary">1 Summary</a>
+<li><a name="toc_Installation" href="#Installation">2 Installing the queueing toolbox</a>
+<ul>
+<li><a href="#Installation-through-Octave-package-management-system">2.1 Installation through Octave package management system</a>
+<li><a href="#Manual-installation">2.2 Manual installation</a>
+<li><a href="#Content-of-the-source-distribution">2.3 Content of the source distribution</a>
+<li><a href="#Using-the-queueing-toolbox">2.4 Using the queueing toolbox</a>
+</li></ul>
+<li><a name="toc_Getting-Started" href="#Getting-Started">3 Introduction and Getting Started</a>
+<ul>
+<li><a href="#Analysis-of-Closed-Networks">3.1 Analysis of Closed Networks</a>
+<li><a href="#Analysis-of-Open-Networks">3.2 Analysis of Open Networks</a>
+</li></ul>
+<li><a name="toc_Markov-Chains" href="#Markov-Chains">4 Markov Chains</a>
+<ul>
+<li><a href="#Discrete_002dTime-Markov-Chains">4.1 Discrete-Time Markov Chains</a>
+<ul>
+<li><a href="#DTMC-Stationary-Probability">4.1.1 Stationary Probability</a>
+<li><a href="#DTMC-First-Passage-Times">4.1.2 First Passage Times</a>
+</li></ul>
+<li><a href="#Continuous_002dTime-Markov-Chains">4.2 Continuous-Time Markov Chains</a>
+<ul>
+<li><a href="#CTMC-Stationary-Probability">4.2.1 Stationary Probability</a>
+<li><a href="#Birth_002dDeath-process">4.2.2 Birth-Death process</a>
+<li><a href="#Expected-Sojourn-Time">4.2.3 Expected Sojourn Time</a>
+<li><a href="#Time_002dAveraged-Expected-Sojourn-Time">4.2.4 Time-Averaged Expected Sojourn Time</a>
+<li><a href="#Expected-Time-to-Absorption">4.2.5 Expected Time to Absorption</a>
+<li><a href="#CTMC-First-Passage-Times">4.2.6 First Passage Times</a>
+</li></ul>
+</li></ul>
+<li><a name="toc_Single-Station-Queueing-Systems" href="#Single-Station-Queueing-Systems">5 Single Station Queueing Systems</a>
+<ul>
+<li><a href="#The-M_002fM_002f1-System">5.1 The M/M/1 System</a>
+<li><a href="#The-M_002fM_002fm-System">5.2 The M/M/m System</a>
+<li><a href="#The-M_002fM_002finf-System">5.3 The M/M/inf System</a>
+<li><a href="#The-M_002fM_002f1_002fK-System">5.4 The M/M/1/K System</a>
+<li><a href="#The-M_002fM_002fm_002fK-System">5.5 The M/M/m/K System</a>
+<li><a href="#The-Asymmetric-M_002fM_002fm-System">5.6 The Asymmetric M/M/m System</a>
+<li><a href="#The-M_002fG_002f1-System">5.7 The M/G/1 System</a>
+<li><a href="#The-M_002fHm_002f1-System">5.8 The M/H_m/1 System</a>
+</li></ul>
+<li><a name="toc_Queueing-Networks" href="#Queueing-Networks">6 Queueing Networks</a>
+<ul>
+<li><a href="#Introduction-to-QNs">6.1 Introduction to QNs</a>
+<ul>
+<li><a href="#Introduction-to-QNs">6.1.1 Single class models</a>
+<li><a href="#Introduction-to-QNs">6.1.2 Multiple class models</a>
+</li></ul>
+<li><a href="#Generic-Algorithms">6.2 Generic Algorithms</a>
+<li><a href="#Algorithms-for-Product_002dForm-QNs">6.3 Algorithms for Product-Form QNs</a>
+<ul>
+<li><a href="#Algorithms-for-Product_002dForm-QNs">6.3.1 Jackson Networks</a>
+<li><a href="#Algorithms-for-Product_002dForm-QNs">6.3.2 The Convolution Algorithm</a>
+<li><a href="#Algorithms-for-Product_002dForm-QNs">6.3.3 Open networks</a>
+<li><a href="#Algorithms-for-Product_002dForm-QNs">6.3.4 Closed Networks</a>
+<li><a href="#Algorithms-for-Product_002dForm-QNs">6.3.5 Mixed Networks</a>
+</li></ul>
+<li><a href="#Algorithms-for-non-Product_002dform-QNs">6.4 Algorithms for non Product-Form QNs</a>
+<li><a href="#Bounds-on-performance">6.5 Bounds on performance</a>
+<li><a href="#Utility-functions">6.6 Utility functions</a>
+<ul>
+<li><a href="#Utility-functions">6.6.1 Open or closed networks</a>
+<li><a href="#Utility-functions">6.6.2 Computation of the visit counts</a>
+<li><a href="#Utility-functions">6.6.3 Other utility functions</a>
+</li></ul>
+</li></ul>
+<li><a name="toc_Contributing-Guidelines" href="#Contributing-Guidelines">Appendix A Contributing Guidelines</a>
+<li><a name="toc_Acknowledgements" href="#Acknowledgements">Appendix B Acknowledgements</a>
+<li><a name="toc_Copying" href="#Copying">Appendix C GNU GENERAL PUBLIC LICENSE</a>
+<li><a name="toc_Concept-Index" href="#Concept-Index">Concept Index</a>
+<li><a name="toc_Function-Index" href="#Function-Index">Function Index</a>
+<li><a name="toc_Author-Index" href="#Author-Index">Author Index</a>
+</li></ul>
+</div>
+
+<div class="node">
+<a name="Top"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Summary">Summary</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#dir">(dir)</a>
+
+</div>
+
+<h2 class="unnumbered">queueing</h2>
+
+<p>This manual documents how to install and run the Queueing Toolbox. 
+It corresponds to version 1.0.0 of the package.
+
+<!--  -->
+<ul class="menu">
+<li><a accesskey="1" href="#Summary">Summary</a>
+<li><a accesskey="2" href="#Installation">Installation</a>:                 Installation of the queueing toolbox. 
+<li><a accesskey="3" href="#Getting-Started">Getting Started</a>:              Getting started with the queueing toolbox. 
+<li><a accesskey="4" href="#Markov-Chains">Markov Chains</a>:                Functions for Markov Chains. 
+<li><a accesskey="5" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>:  Functions for single-station queueing systems. 
+<li><a accesskey="6" href="#Queueing-Networks">Queueing Networks</a>:            Functions for queueing networks. 
+<li><a accesskey="7" href="#Contributing-Guidelines">Contributing Guidelines</a>:      How to contribute. 
+<li><a accesskey="8" href="#Acknowledgements">Acknowledgements</a>:             People who contributed to the queueing toolbox. 
+<li><a accesskey="9" href="#Copying">Copying</a>:                      The GNU General Public License. 
+<li><a href="#Concept-Index">Concept Index</a>:                An item for each concept. 
+<li><a href="#Function-Index">Function Index</a>:               An item for each function. 
+<li><a href="#Author-Index">Author Index</a>:                 An item for each author. 
+</ul>
+
+<!--  -->
+<!-- DO NOT EDIT!  Generated automatically by munge-texi. -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla -->
+<!-- This file is part of the queueing toolbox, a Queueing Networks -->
+<!-- analysis package for GNU Octave. -->
+<!-- The queueing toolbox 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 3 of -->
+<!-- the License, or (at your option) any later version. -->
+<!-- The queueing toolbox 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. -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with the queueing toolbox; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Summary"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Installation">Installation</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Top">Top</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">1 Summary</h2>
+
+<p>This document describes the <code>queueing</code> toolbox for GNU Octave
+(<code>queueing</code> in short). The <code>queueing</code> toolbox, previously
+known as <code>qnetworks</code>, is a collection of functions written in GNU
+Octave for analyzing queueing networks and Markov
+chains. Specifically, <code>queueing</code> contains functions for analyzing
+Jackson networks, open, closed or mixed product-form BCMP networks,
+and computation of performance bounds. The following algorithms have
+been implemented
+
+     <ul>
+<li>Convolution for closed, single-class product-form networks
+with load-dependent service centers;
+
+     <li>Exact and approximate Mean Value Analysis (MVA) for single and
+multiple class product-form closed networks;
+
+     <li>MVA for mixed, multiple class product-form networks
+with load-independent service centers;
+
+     <li>Approximate MVA for closed, single-class networks with blocking
+(MVABLO algorithm by F. Akyildiz);
+
+     <li>Computation of Asymptotic Bounds, Balanced System Bounds
+and Geometric Bounds;
+
+   </ul>
+
+<p class="noindent"><code>queueing</code>
+provides functions for analyzing the following kind of single-station
+queueing systems:
+
+     <ul>
+<li>M/M/1
+<li>M/M/m
+<li>M/M/\infty
+<li>M/M/1/k single-server, finite capacity system
+<li>M/M/m/k multiple-server, finite capacity system
+<li>Asymmetric M/M/m
+<li>M/G/1 (general service time distribution)
+<li>M/H_m/1 (Hyperexponential service time distribution)
+</ul>
+
+   <p>Functions for Markov chain analysis are also provided (discrete and
+continuous time Markov chains are supported):
+
+     <ul>
+<li>Birth-death process;
+<li>Computation of transient and steady-state occupancy probabilities;
+<li>Computation of mean time to absorption;
+<li>Computation of time-averages sojourn time. 
+<li>Computation of mean passage times
+
+   </ul>
+
+   <p>The <code>queueing</code> toolbox is distributed under the terms of the GNU
+General Public License (GPL), version 3 or later
+(see <a href="#Copying">Copying</a>). You are encouraged to share this software with
+others, and make this package more useful by contributing additional
+functions and reporting problems. See <a href="#Contributing-Guidelines">Contributing Guidelines</a>.
+
+   <p>If you use the <code>queueing</code> toolbox in a technical paper, please
+cite it as:
+
+   <blockquote>
+Moreno Marzolla, <em>The qnetworks Toolbox: A Software Package for
+Queueing Networks Analysis</em>. Khalid Al-Begain, Dieter Fiems and
+William J. Knottenbelt, Editors, Proceedings 17th International
+Conference on Analytical and Stochastic Modeling Techniques and
+Applications (ASMTA 2010) Cardiff, UK, June 14&ndash;16, 2010, volume 6148
+of Lecture Notes in Computer Science, Springer, pp. 102&ndash;116, ISBN
+978-3-642-13567-5
+</blockquote>
+
+   <p>If you use BibTeX, this is the citation block:
+
+<pre class="verbatim">@inproceedings{queueing,
+  author    = {Moreno Marzolla},
+  title     = {The qnetworks Toolbox: A Software Package for Queueing 
+               Networks Analysis},
+  booktitle = {Analytical and Stochastic Modeling Techniques and 
+               Applications, 17th International Conference, 
+               ASMTA 2010, Cardiff, UK, June 14-16, 2010. Proceedings},
+  editor    = {Khalid Al-Begain and Dieter Fiems and William J. Knottenbelt},
+  year      = {2010},
+  publisher = {Springer},
+  series    = {Lecture Notes in Computer Science},
+  volume    = {6148},
+  pages     = {102--116},
+  ee        = {http://dx.doi.org/10.1007/978-3-642-13568-2_8},
+  isbn      = {978-3-642-13567-5}
+}
+</pre>
+
+   <p>An early draft of the paper above is available as Technical Report
+<a href="http://www.informatica.unibo.it/ricerca/ublcs/2010/UBLCS-2010-04">UBLCS-2010-04</a>, February 2010, Department of Computer Science,
+University of Bologna, Italy.
+
+<!-- DO NOT EDIT!  Generated automatically by munge-texi. -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla -->
+<!-- This file is part of the queueing toolbox, a Queueing Networks -->
+<!-- analysis package for GNU Octave. -->
+<!-- The queueing toolbox 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 3 of -->
+<!-- the License, or (at your option) any later version. -->
+<!-- The queueing toolbox 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. -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with the queueing toolbox; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Installation"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Getting-Started">Getting Started</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Summary">Summary</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">2 Installing the queueing toolbox</h2>
+
+<ul class="menu">
+<li><a accesskey="1" href="#Installation-through-Octave-package-management-system">Installation through Octave package management system</a>
+<li><a accesskey="2" href="#Manual-installation">Manual installation</a>
+<li><a accesskey="3" href="#Content-of-the-source-distribution">Content of the source distribution</a>
+<li><a accesskey="4" href="#Using-the-queueing-toolbox">Using the queueing toolbox</a>
+</ul>
+
+<div class="node">
+<a name="Installation-through-Octave-package-management-system"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Manual-installation">Manual installation</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Installation">Installation</a>
+
+</div>
+
+<h3 class="section">2.1 Installation through Octave package management system</h3>
+
+<p>The most recent version of <code>queueing</code> is 1.0.0 and can
+be downloaded from
+
+   <p><a href="http://www.moreno.marzolla.name/software/queueing/queueing-1.0.0.tar.gz">http://www.moreno.marzolla.name/software/queueing/queueing-1.0.0.tar.gz</a>
+
+   <p>To install <code>queueing</code> in the system-wide location, such that all
+functions are automatically available when Octave starts, you can use
+&lsquo;<samp><span class="samp">pkg install</span></samp>&rsquo; command. At the Octave prompt, type the following:
+
+<pre class="example">     octave:1&gt; <kbd>pkg install queueing-1.0.0.tar.gz</kbd>
+</pre>
+   <p>(Note: you may need to start Octave as root in order to allow the
+installation to copy the files to the target locations). After this,
+all functions will be readily available each time Octave starts,
+without the need to tweak the search path. To uninstall
+<code>queueing</code>, use the &lsquo;<samp><span class="samp">pkg uninstall queueing</span></samp>&rsquo; command.
+
+   <p>If you do not have root access, you can do a local installation by
+issuing the following command at the Octave prompt:
+
+<pre class="example">     octave:1&gt; <kbd>pkg install -local queueing-1.0.0.tar.gz</kbd>
+</pre>
+   <p>This will install <code>queueing</code> within the user's home directory,
+and the package will be available to that user only. <strong>Note:</strong>
+Octave version 3.2.3 as shipped with Ubuntu 10.04 seems to ignore
+<code>-local</code> and always tries to install the package on the system
+directory.
+
+<div class="node">
+<a name="Manual-installation"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Content-of-the-source-distribution">Content of the source distribution</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Installation-through-Octave-package-management-system">Installation through Octave package management system</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Installation">Installation</a>
+
+</div>
+
+<h3 class="section">2.2 Manual installation</h3>
+
+<p>If you want to install <code>queueing</code> in a custom location, you can
+download the source tarball from the URL above, and unpack it
+somewhere:
+
+<pre class="example">     <kbd>tar xfz queueing-1.0.0.tar.gz</kbd>
+     <kbd>cd queueing-1.0.0/</kbd>
+</pre>
+   <p>Copy all <code>.m</code> files from the <samp><span class="file">inst/</span></samp> directory to some
+target location. Then, you can start Octave with the <samp><span class="option">-p</span></samp>
+option to add the target location to the search path, so that Octave
+will find all <code>queueing</code> functions automatically:
+
+<pre class="example">     <kbd>octave -p </kbd><em>/path/to/queueing</em>
+</pre>
+   <p>For example, if all <code>queueing</code> m-files are in
+<samp><span class="file">/usr/local/queueing</span></samp>, you can start Octave as follows:
+
+<pre class="example">     <kbd>octave -p </kbd><samp><span class="file">/usr/local/queueing</span></samp>
+</pre>
+   <p>If you want, you can add the following line to <samp><span class="file">~/.octaverc</span></samp>:
+
+<pre class="example">     <kbd>addpath("</kbd><em>/path/to/queueing</em><kbd>");</kbd>
+</pre>
+   <p class="noindent">so that the path <samp><span class="file">/usr/local/queueing</span></samp> is automatically
+added to the search path each time Octave is started, and you no
+longer need to specify the <samp><span class="option">-p</span></samp> option on the command line.
+
+<div class="node">
+<a name="Content-of-the-source-distribution"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Using-the-queueing-toolbox">Using the queueing toolbox</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Manual-installation">Manual installation</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Installation">Installation</a>
+
+</div>
+
+<h3 class="section">2.3 Content of the source distribution</h3>
+
+<p>The <code>queueing</code> source distribution contains the following
+subdirectories:
+
+     <dl>
+<dt><samp><span class="file">doc/</span></samp><dd>Documentation source. Most of the documentation is extracted from the
+comment blocks of individual function files from the <samp><span class="file">inst/</span></samp>
+directory.
+
+     <br><dt><samp><span class="file">inst/</span></samp><dd>This directory contains the <tt>m</tt>-files which implement the
+various Queueing Network algorithms provided by <code>queueing</code>. As a
+notational convention, the names of source files containing functions
+for Queueing Networks start with the &lsquo;<samp><span class="samp">qn</span></samp>&rsquo; prefix; the name of
+source files containing functions for Continuous-Time Markov Chains
+(CTMSs) start with the &lsquo;<samp><span class="samp">ctmc</span></samp>&rsquo; prefix, and the names of files
+containing functions for Discrete-Time Markov Chains (DTMCs) start
+with the &lsquo;<samp><span class="samp">dtmc</span></samp>&rsquo; prefix.
+
+     <br><dt><samp><span class="file">test/</span></samp><dd>This directory contains the test functions used to invoke all tests on
+all function files.
+
+     <br><dt><samp><span class="file">scripts/</span></samp><dd>This directory contains some utility scripts mostly from GNU Octave,
+which extract the documentation from the specially-formatted comments
+in the <tt>m</tt>-files.
+
+     <br><dt><samp><span class="file">examples/</span></samp><dd>This directory contains examples which are automatically extracted
+from the &lsquo;<samp><span class="samp">demo</span></samp>&rsquo; blocks of the function files.
+
+     <br><dt><samp><span class="file">broken/</span></samp><dd>This directory contains function files which are either not working
+properly, or need additional testing before they can be moved to the
+<samp><span class="file">inst/</span></samp> directory.
+
+   </dl>
+
+   <p>The <code>queueing</code> package ships with a Makefile which can be used
+to produce the documentation (in PDF and HTML format), and
+automatically execute all function tests. Specifically, the following
+targets are defined:
+
+     <dl>
+<dt><code>all</code><dd>Running &lsquo;<samp><span class="samp">make</span></samp>&rsquo; (or &lsquo;<samp><span class="samp">make all</span></samp>&rsquo;) on the top-level directory
+builds the programs used to extract the documentation from the
+comments embedded in the <tt>m</tt>-files, and then produce the
+documentation in PDF and HTML format (<samp><span class="file">doc/queueing.pdf</span></samp> and
+<samp><span class="file">doc/queueing.html</span></samp>, respectively).
+
+     <br><dt><code>check</code><dd>Running &lsquo;<samp><span class="samp">make check</span></samp>&rsquo; will execute all tests contained in the
+<tt>m</tt>-files. If you modify the code of any function in the
+<samp><span class="file">inst/</span></samp> directory, you should run the tests to ensure that no
+errors have been introduced. You are also encouraged to contribute new
+tests, especially for functions which are not adequately validated.
+
+     <br><dt><code>clean</code><dt><code>distclean</code><dt><code>dist</code><dd>The &lsquo;<samp><span class="samp">make clean</span></samp>&rsquo;, &lsquo;<samp><span class="samp">make distclean</span></samp>&rsquo; and &lsquo;<samp><span class="samp">make dist</span></samp>&rsquo;
+commands are used to clean up the source directory and prepare the
+distribution archive in compressed tar format.
+
+   </dl>
+
+<div class="node">
+<a name="Using-the-queueing-toolbox"></a>
+<p><hr>
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Content-of-the-source-distribution">Content of the source distribution</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Installation">Installation</a>
+
+</div>
+
+<h3 class="section">2.4 Using the queueing toolbox</h3>
+
+<p>You can use all functions by simply invoking their name with the
+appropriate parameters; the <code>queueing</code> package should display an
+error message in case of missing/wrong parameters. You can display the
+help text for any function using the <samp><span class="command">help</span></samp> command. For
+example:
+
+<pre class="example">     octave:2&gt; <kbd>help qnmvablo</kbd>
+</pre>
+   <p>prints the documentation for the <samp><span class="command">qnmvablo</span></samp> function. 
+Additional information can be found in the <code>queueing</code> manual,
+which is available in PDF format in <samp><span class="file">doc/queueing.pdf</span></samp> and in
+HTML format in <samp><span class="file">doc/queueing.html</span></samp>.
+
+   <p>Within GNU Octave, you can also run the test and demo blocks
+associated to the functions, using the <samp><span class="command">test</span></samp> and
+<samp><span class="command">demo</span></samp> commands respectively. To run all the tests of, say,
+the <samp><span class="command">qnmvablo</span></samp> function:
+
+<pre class="example">     octave:3&gt; <kbd>test qnmvablo</kbd>
+     -| PASSES 4 out of 4 tests
+</pre>
+   <p>To execute the demos of the <samp><span class="command">qnclosed</span></samp> function, use the
+following:
+
+<pre class="example">     octave:4&gt; <kbd>demo qnclosed</kbd>
+</pre>
+   <!-- DO NOT EDIT!  Generated automatically by munge-texi. -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla -->
+<!-- This file is part of the queueing toolbox, a Queueing Networks -->
+<!-- analysis package for GNU Octave. -->
+<!-- The queueing toolbox 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 3 of -->
+<!-- the License, or (at your option) any later version. -->
+<!-- The queueing toolbox 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. -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with the queueing toolbox; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Getting-Started"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Markov-Chains">Markov Chains</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Installation">Installation</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">3 Introduction and Getting Started</h2>
+
+<ul class="menu">
+<li><a accesskey="1" href="#Analysis-of-Closed-Networks">Analysis of Closed Networks</a>
+<li><a accesskey="2" href="#Analysis-of-Open-Networks">Analysis of Open Networks</a>
+</ul>
+
+<p>In this chapter we give some usage examples of the <code>queueing</code>
+package. The reader is assumed to be familiar with Queueing Networks
+(although some basic terminology and notation will be given
+here). Additional usage examples are embedded in most of the function
+files; to display and execute the demos associated with function
+<em>fname</em> you can type <samp><span class="command">demo </span><em>fname</em></samp> at the Octave
+prompt. For example
+
+<pre class="example">     <kbd>demo qnclosed</kbd>
+</pre>
+   <p class="noindent">executes all demos (if any) for the <samp><span class="command">qnclosed</span></samp> function.
+
+<div class="node">
+<a name="Analysis-of-Closed-Networks"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Analysis-of-Open-Networks">Analysis of Open Networks</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.1 Analysis of Closed Networks</h3>
+
+<p>Let us consider a simple closed network with K=3 service
+centers. Each center is of type M/M/1&ndash;FCFS. We denote with
+S_i the average service time at center i, i=1, 2,
+3. Let S_1 = 1.0, S_2 = 2.0 and S_3 = 0.8. The
+routing of jobs within the network is described with a <em>routing
+probability matrix</em> P. Specifically, a request completing
+service at center i is enqueued at center j with
+probability P_ij.  Let us assume the following routing
+probability matrix:
+
+<pre class="example">         [ 0  0.3  0.7 ]
+     P = [ 1  0    0   ]
+         [ 1  0    0   ]
+</pre>
+   <p>For example, according to matric P a job completing service at
+center 1 is routed to center 2 with probability 0.3, and is routed to
+center 3 with probability 0.7.
+
+   <p>The network above can be analyzed with the <samp><span class="command">qnclosed</span></samp>
+function; if there is just a single class of requests, as in the
+example above, <samp><span class="command">qnclosed</span></samp> calls <samp><span class="command">qnclosedsinglemva</span></samp>
+which implements the Mean Value Analysys (MVA) algorithm for
+single-class, product-form network.
+
+   <p><samp><span class="command">qnclosed</span></samp> requires the following parameters:
+
+     <dl>
+<dt><var>N</var><dd>Number of requests in the network (since we are considering a closed
+network, the number of requests is fixed)
+
+     <br><dt><var>S</var><dd>Array of average service times at the centers: <var>S</var><code>(k)</code> is
+the average service time at center k.
+
+     <br><dt><var>V</var><dd>Array of visit ratios: <var>V</var><code>(k)</code> is the average number of
+visits to center k.
+
+   </dl>
+
+   <p>As can be seen, we must compute the <em>visit ratios</em> (or visit
+counts) V_k for each center k. The visit counts satisfy
+the following equations:
+
+<pre class="example">     V_j = sum_i V_i P_ij
+</pre>
+   <p>We can compute V_k from the routing probability matrix
+P_ij using the <samp><span class="command">qnvisits</span></samp> function:
+
+<pre class="example">     <kbd>P = [0 0.3 0.7; 1 0 0; 1 0 0];</kbd>
+     <kbd>V = qnvisits(P)</kbd>
+        &rArr; V = 1.00000 0.30000 0.70000
+</pre>
+   <p>We can check that the computed values satisfy the above equation by
+evaluating the following expression:
+
+<pre class="example">     <kbd>V*P</kbd>
+          &rArr; ans = 1.00000 0.30000 0.70000
+</pre>
+   <p class="noindent">which is equal to V. 
+Hence, we can analyze the network for a given population size N
+(for example, N=10) as follows:
+
+<pre class="example">     <kbd>N = 10;</kbd>
+     <kbd>S = [1 2 0.8];</kbd>
+     <kbd>P = [0 0.3 0.7; 1 0 0; 1 0 0];</kbd>
+     <kbd>V = qnvisits(P);</kbd>
+     <kbd>[U R Q X] = qnclosed( N, S, V )</kbd>
+        &rArr; U = 0.99139 0.59483 0.55518
+        &rArr; R = 7.4360  4.7531  1.7500
+        &rArr; Q = 7.3719  1.4136  1.2144
+        &rArr; X = 0.99139 0.29742 0.69397
+</pre>
+   <p>The output of <samp><span class="command">qnclosed</span></samp> includes the vector of utilizations
+U_k at center k, response time R_k, average
+number of customers Q_k and throughput X_k. In our
+example, the throughput of center 1 is X_1 = 0.99139, and the
+average number of requests in center 3 is Q_3 = 1.2144. The
+utilization of center 1 is U_1 = 0.99139, which is the higher
+value among the service centers. Tus, center 1 is the <em>bottleneck
+device</em>.
+
+   <p>This network can also be analyzed with the <samp><span class="command">qnsolve</span></samp>
+function. <samp><span class="command">qnsolve</span></samp> can handle open, closed or mixed networks,
+and allows the network to be described in a very flexible way.  First,
+let <var>Q1</var>, <var>Q2</var> and <var>Q3</var> be the variables describing the
+service centers. Each variable is instantiated with the
+<samp><span class="command">qnmknode</span></samp> function.
+
+<pre class="example">     <kbd>Q1 = qnmknode( "m/m/m-fcfs", 1 );</kbd>
+     <kbd>Q2 = qnmknode( "m/m/m-fcfs", 2 );</kbd>
+     <kbd>Q3 = qnmknode( "m/m/m-fcfs", 0.8 );</kbd>
+</pre>
+   <p>The first parameter of <samp><span class="command">qnmknode</span></samp> is a string describing the
+type of the node. Here we use <code>"m/m/m-fcfs"</code> to denote a
+M/M/m&ndash;FCFS center. The second parameter gives the average
+service time. An optional third parameter can be used to specify the
+number m of service centers. If omitted, it is assumed
+m=1 (single-server node).
+
+   <p>Now, the network can be analyzed as follows:
+
+<pre class="example">     <kbd>N = 10;</kbd>
+     <kbd>V = [1 0.3 0.7];</kbd>
+     <kbd>[U R Q X] = qnsolve( "closed", N, { Q1, Q2, Q3 }, V )</kbd>
+        &rArr; U = 0.99139 0.59483 0.55518
+        &rArr; R = 7.4360  4.7531  1.7500
+        &rArr; Q = 7.3719  1.4136  1.2144
+        &rArr; X = 0.99139 0.29742 0.69397
+</pre>
+   <p>Of course, we get exactly the same results. Other functions can be used
+for closed networks, see <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a>.
+
+<div class="node">
+<a name="Analysis-of-Open-Networks"></a>
+<p><hr>
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Analysis-of-Closed-Networks">Analysis of Closed Networks</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Getting-Started">Getting Started</a>
+
+</div>
+
+<h3 class="section">3.2 Analysis of Open Networks</h3>
+
+<p>Open networks can be analyzed in a similar way. Let us consider
+an open network with K=3 service centers, and routing
+probability matrix as follows:
+
+<pre class="example">         [ 0  0.3  0.5 ]
+     P = [ 1  0    0   ]
+         [ 1  0    0   ]
+</pre>
+   <p>In this network, requests can leave the system from center 1 with
+probability (1-(0.3+0.5) = 0.2. We suppose that external jobs
+arrive at center 1 with rate \lambda_1 = 0.15; there are no
+arrivals at centers 2 and 3.
+
+   <p>Similarly to closed networks, we first need to compute the visit
+counts V_k to center k. Again, we use the
+<samp><span class="command">qnvisits</span></samp> function as follows:
+
+<pre class="example">     <kbd>P = [0 0.3 0.5; 1 0 0; 1 0 0];</kbd>
+     <kbd>lambda = [0.15 0 0];</kbd>
+     <kbd>V = qnvisits(P, lambda)</kbd>
+        &rArr; V = 5.00000 1.50000 2.50000
+</pre>
+   <p class="noindent">where <var>lambda</var><code>(k)</code> is the arrival rate at center k,
+and <var>P</var> is the routing matrix. The visit counts V_k for
+open networks satisfy the following equation:
+
+<pre class="example">     V_j = sum_i V_i P_ij
+</pre>
+   <p>where P_0j is the probability of an external arrival to
+center j. This can be computed as:
+
+   <p>Assuming the same service times as in the previous example, the
+network can be analyzed with the <samp><span class="command">qnopen</span></samp> function, as
+follows:
+
+<pre class="example">     <kbd>S = [1 2 0.8];</kbd>
+     <kbd>[U R Q X] = qnopen( sum(lambda), S, V )</kbd>
+        &rArr; U = 0.75000 0.45000 0.30000
+        &rArr; R = 4.0000  3.6364  1.1429
+        &rArr; Q = 3.00000 0.81818 0.42857
+        &rArr; X = 0.75000 0.22500 0.37500
+</pre>
+   <p>The first parameter of the <samp><span class="command">qnopen</span></samp> function is the (scalar)
+aggregate arrival rate.
+
+   <p>Again, it is possible to use the <samp><span class="command">qnsolve</span></samp> high-level function:
+
+<pre class="example">     <kbd>Q1 = qnmknode( "m/m/m-fcfs", 1 );</kbd>
+     <kbd>Q2 = qnmknode( "m/m/m-fcfs", 2 );</kbd>
+     <kbd>Q3 = qnmknode( "m/m/m-fcfs", 0.8 );</kbd>
+     <kbd>lambda = [0.15 0 0];</kbd>
+     <kbd>[U R Q X] = qnsolve( "open", sum(lambda), { Q1, Q2, Q3 }, V )</kbd>
+        &rArr; U = 0.75000 0.45000 0.30000
+        &rArr; R = 4.0000  3.6364  1.1429
+        &rArr; Q = 3.00000 0.81818 0.42857
+        &rArr; X = 0.75000 0.22500 0.37500
+</pre>
+   <!-- @node Markov Chains Analysis -->
+<!-- @section Markov Chains Analysis -->
+<!-- @subsection Discrete-Time Markov Chains -->
+<!-- (TODO) -->
+<!-- @subsection Continuous-Time Markov Chains -->
+<!-- (TODO) -->
+<!-- DO NOT EDIT!  Generated automatically by munge-texi. -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla -->
+<!-- This file is part of the queueing toolbox, a Queueing Networks -->
+<!-- analysis package for GNU Octave. -->
+<!-- The queueing toolbox 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 3 of -->
+<!-- the License, or (at your option) any later version. -->
+<!-- The queueing toolbox 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. -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with the queueing toolbox; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Markov-Chains"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Getting-Started">Getting Started</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">4 Markov Chains</h2>
+
+<ul class="menu">
+<li><a accesskey="1" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>
+<li><a accesskey="2" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+</ul>
+
+<div class="node">
+<a name="Discrete-Time-Markov-Chains"></a>
+<a name="Discrete_002dTime-Markov-Chains"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Markov-Chains">Markov Chains</a>
+
+</div>
+
+<h3 class="section">4.1 Discrete-Time Markov Chains</h3>
+
+<ul class="menu">
+<li><a accesskey="1" href="#DTMC-Stationary-Probability">DTMC Stationary Probability</a>
+<li><a accesskey="2" href="#DTMC-First-Passage-Times">DTMC First Passage Times</a>
+</ul>
+
+<div class="node">
+<a name="DTMC-Stationary-Probability"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#DTMC-First-Passage-Times">DTMC First Passage Times</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">4.1.1 Stationary Probability</h4>
+
+<p><a name="doc_002ddtmc"></a>
+
+<div class="defun">
+&mdash; Function File: <var>p</var> = <b>dtmc</b> (<var>P</var>)<var><a name="index-dtmc-1"></a></var><br>
+&mdash; Function File: <var>p</var> = <b>dtmc</b> (<var>P, n, p0</var>)<var><a name="index-dtmc-2"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-discrete-time-3"></a><a name="index-Discrete-time-Markov-chain-4"></a><a name="index-Markov-chain_002c-stationary-probabilities-5"></a><a name="index-Stationary-probabilities-6"></a>
+With a single argument, compute the steady-state probability vector
+<var>p</var><code>(1), ..., </code><var>p</var><code>(N)</code> for a
+Discrete-Time Markov Chain given the N \times N transition
+probability matrix <var>P</var>. With three arguments, compute the
+probability vector <var>p</var><code>(1), ..., </code><var>p</var><code>(N)</code>
+after <var>n</var> steps, given initial probability vector <var>p0</var> at
+time 0.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd><var>P</var><code>(i,j)</code> is the transition probability from state i
+to state j. <var>P</var> must be an irreducible stochastic matrix,
+which means that the sum of each row must be 1 (\sum_j=1^N P_i j = 1), and the rank of
+<var>P</var> must be equal to its dimension.
+
+          <br><dt><var>n</var><dd>Step at which to compute the transient probability
+
+          <br><dt><var>p0</var><dd><var>p0</var><code>(i)</code> is the probability that at step 0 the system
+is in state i.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>p</var><dd>If this function is invoked with a single argument,
+<var>p</var><code>(i)</code> is the steady-state probability that the system is
+in state i. <var>p</var> satisfies the equations p = p\bf P and \sum_i=1^N p_i = 1. If this function is invoked
+with three arguments, <var>p</var><code>(i)</code> is the marginal probability
+that the system is in state i at step <var>n</var>,
+given the initial probabilities <var>p0</var><code>(i)</code> that the initial state is
+i.
+
+        </dl>
+
+        </blockquote></div>
+
+<div class="node">
+<a name="DTMC-First-Passage-Times"></a>
+<p><hr>
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#DTMC-Stationary-Probability">DTMC Stationary Probability</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">4.1.2 First Passage Times</h4>
+
+<p>The First Passage Time M_i j is defined as the average
+number of transitions needed to visit state j for the first
+time, starting from state i. Matrix \bf M satisfies the
+property that
+
+<pre class="example">                ___
+               \
+     M_ij = 1 + &gt;   P_ij * M_kj
+               /___
+               k!=j
+</pre>
+   <p><a name="doc_002ddtmc_005ffpt"></a>
+
+<div class="defun">
+&mdash; Function File: <var>M</var> = <b>dtmc_fpt</b> (<var>P</var>)<var><a name="index-dtmc_005ffpt-7"></a></var><br>
+&mdash; Function File: <var>m</var> = <b>dtmc_fpt</b> (<var>P, i, j</var>)<var><a name="index-dtmc_005ffpt-8"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-discrete-time-9"></a><a name="index-First-passage-times-10"></a>
+If called with a single argument, computes the mean first passage
+times <var>M</var><code>(i,j)</code>, that are the average number of transitions before
+state <var>j</var> is reached, starting from state <var>i</var>, for all
+1 \leq i, j \leq N. If called with three arguments, returns
+the single value <var>m</var><code> = </code><var>M</var><code>(i,j)</code>.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd><var>P</var><code>(i,j)</code> is the transition probability from state i
+to state j. <var>P</var> must be an irreducible stochastic matrix,
+which means that the sum of each row must be 1 (\sum_j=1^N
+P_i j = 1), and the rank of <var>P</var> must be equal to its
+dimension.
+
+          <br><dt><var>i</var><dd>Initial state.
+
+          <br><dt><var>j</var><dd>Destination state. If <var>j</var> is a vector, returns the mean first passage
+time to any state in <var>j</var>.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>M</var><dd>If this function is called with a single argument, the result
+<var>M</var><code>(i,j)</code> is the average number of transitions before state
+<var>j</var> is reached for the first time, starting from state <var>i</var>.
+
+          <br><dt><var>m</var><dd>If this function is called with three arguments, the result <var>m</var>
+is the average number of transitions before state <var>j</var> is visited
+for the first time, starting from state <var>i</var>.
+
+        </dl>
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Continuous-Time-Markov-Chains"></a>
+<a name="Continuous_002dTime-Markov-Chains"></a>
+<p><hr>
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Markov-Chains">Markov Chains</a>
+
+</div>
+
+<h3 class="section">4.2 Continuous-Time Markov Chains</h3>
+
+<ul class="menu">
+<li><a accesskey="1" href="#CTMC-Stationary-Probability">CTMC Stationary Probability</a>
+<li><a accesskey="2" href="#Birth_002dDeath-process">Birth-Death process</a>
+<li><a accesskey="3" href="#Expected-Sojourn-Time">Expected Sojourn Time</a>
+<li><a accesskey="4" href="#Time_002dAveraged-Expected-Sojourn-Time">Time-Averaged Expected Sojourn Time</a>
+<li><a accesskey="5" href="#Expected-Time-to-Absorption">Expected Time to Absorption</a>
+<li><a accesskey="6" href="#CTMC-First-Passage-Times">CTMC First Passage Times</a>
+</ul>
+
+<div class="node">
+<a name="CTMC-Stationary-Probability"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Birth_002dDeath-process">Birth-Death process</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">4.2.1 Stationary Probability</h4>
+
+<p><a name="doc_002dctmc"></a>
+
+<div class="defun">
+&mdash; Function File: <var>p</var> = <b>ctmc</b> (<var>Q</var>)<var><a name="index-ctmc-11"></a></var><br>
+&mdash; Function File: <var>p</var> = <b>ctmc</b> (<var>Q, t. q0</var>)<var><a name="index-ctmc-12"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-13"></a><a name="index-Continuous-time-Markov-chain-14"></a><a name="index-Markov-chain_002c-state-occupancy-probabilities-15"></a><a name="index-Stationary-probabilities-16"></a>
+With a single argument, compute the stationary state occupancy
+probability vector <var>p</var>(1), <small class="dots">...</small>, <var>p</var>(N) for a
+Continuous-Time Markov Chain with infinitesimal generator matrix
+<var>Q</var> of size  N \times N. With three arguments, compute the
+state occupancy probabilities <var>p</var>(1), <small class="dots">...</small>, <var>p</var>(N) at time
+<var>t</var>, given initial state occupancy probabilities <var>p0</var> at time
+0.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>Q</var><dd>Infinitesimal generator matrix. <var>Q</var> is a N \times N square
+matrix where <var>Q</var><code>(i,j)</code> is the transition rate from state
+i to state j, for 1 &le; i \neq j &le; N. 
+Transition rates must be nonnegative, and \sum_j=1^N Q_i j = 0
+
+          <br><dt><var>t</var><dd>Time at which to compute the transient probability
+
+          <br><dt><var>p0</var><dd><var>p0</var><code>(i)</code> is the probability that the system
+is in state i at time 0 .
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>p</var><dd>If this function is invoked with a single argument,
+<var>p</var><code>(i)</code> is the steady-state probability that the system is
+in state i, i = 1, <small class="dots">...</small>, N. The vector <var>p</var>
+satisfies the equation p\bf Q = 0 and \sum_i=1^N p_i = 1. 
+If this function is invoked with three arguments, <var>p</var><code>(i)</code>
+is the probability that the system is in state i at time <var>t</var>,
+given the initial occupancy probabilities <var>q0</var>.
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+   <p>Consider a two-state CTMC such that transition rates between states
+are equal to 1. This can be solved as follows:
+
+<pre class="example"><pre class="verbatim">      Q = [ -1  1; \
+             1 -1  ];
+      q = ctmc(Q)</pre>    &rArr; q = 0.50000   0.50000
+</pre>
+   <div class="node">
+<a name="Birth-Death-process"></a>
+<a name="Birth_002dDeath-process"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Expected-Sojourn-Time">Expected Sojourn Time</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#CTMC-Stationary-Probability">CTMC Stationary Probability</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">4.2.2 Birth-Death process</h4>
+
+<p><a name="doc_002dctmc_005fbd"></a>
+
+<div class="defun">
+&mdash; Function File: <var>p</var> = <b>ctmc_bd</b> (<var>birth, death</var>)<var><a name="index-ctmc_005fbd-17"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-18"></a><a name="index-Birth_002ddeath-process-19"></a>
+Compute the steady-state solution of a birth-death process with state
+space (1, <small class="dots">...</small>, N).
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>birth</var><dd>Vector with N-1 elements, where <var>birth</var><code>(i)</code> is the
+transition rate from state i to state i+1.
+
+          <br><dt><var>death</var><dd>Vector with N-1 elements, where <var>death</var><code>(i)</code> is the
+transition rate from state i+1 to state i.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>p</var><dd><var>p</var><code>(i)</code> is the steady-state probability that the system is
+in state i, i=1, <small class="dots">...</small>, N.
+
+        </dl>
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Expected-Sojourn-Time"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Time_002dAveraged-Expected-Sojourn-Time">Time-Averaged Expected Sojourn Time</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Birth_002dDeath-process">Birth-Death process</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">4.2.3 Expected Sojourn Time</h4>
+
+<p>Given a N state continuous-time Markov Chain with infinitesimal
+generator matrix \bf Q, we define the vector \bf L(t) =
+(L_1(t), L_2(t), \ldots L_N(t)) such that L_i(t) is the
+expected sojourn time in state i during the interval
+[0,t), assuming that the initial occupancy probability at time
+0 was \bf \pi(0). Then, \bf L(t) is the solution of
+the following differential equation:
+
+<pre class="example">      dL
+      --(t) = L(t) Q + pi(0),    L(0) = 0
+      dt
+</pre>
+   <p>The function <code>ctmc_exps</code> can be used to compute \bf
+L(t), by using the <code>lsode</code> Octave function to solve the above
+linear differential equation.
+
+   <p><a name="doc_002dctmc_005fexps"></a>
+
+<div class="defun">
+&mdash; Function File: <var>L</var> = <b>ctmc_exps</b> (<var>Q, tt, p</var>)<var><a name="index-ctmc_005fexps-20"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-21"></a><a name="index-Expected-sojourn-time-22"></a>
+Compute the expected total time <var>L</var><code>(t,j)</code> spent in state
+j during the time interval <code>[0,</code><var>tt</var><code>(t))</code>, assuming
+that at time 0 the state occupancy probability was <var>p</var>.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>Q</var><dd>Infinitesimal generator matrix. <var>Q</var><code>(i,j)</code> is the transition
+rate from state i to state j,
+1 &le; i \neq j &le; N. The matrix <var>Q</var> must also satisfy the
+condition <code>sum(</code><var>Q</var><code>,2) == 0</code>
+
+          <br><dt><var>tt</var><dd>This parameter is a vector used for numerical integration. The first
+element <var>tt</var><code>(1)</code> must be 0, and the last element
+<var>tt</var><code>(end)</code> must be the upper bound of the interval
+[0,t) of interest (<var>tt</var><code>(end) == t</code>).
+
+          <br><dt><var>p</var><dd><var>p</var><code>(i)</code> is the probability that at time 0 the system was in
+state i, for all i = 1, <small class="dots">...</small>, N
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>L</var><dd><var>L</var><code>(t,j)</code> is the expected time spent in state j
+during the interval <code>[0,</code><var>tt</var><code>(t))</code>. <code>1 &le; </code><var>t</var><code> &le; length(</code><var>tt</var><code>)</code>
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+   <p>Let us consider a pure-birth, 4-states CTMC such that the transition
+rate from state i to state i+1 is \lambda_i = i
+\lambda (i=1, 2, 3), with \lambda = 0.5. The following
+code computes the expected sojourn time in state i,
+given the initial occupancy probability p_0=(1,0,0,0).
+
+<pre class="example"><pre class="verbatim">      lambda = 0.5;
+      N = 4;
+      birth = lambda*linspace(1,N-1,N-1);
+      death = zeros(1,N-1);
+      Q = diag(birth,1)+diag(death,-1);
+      Q -= diag(sum(Q,2));
+      tt = linspace(0,10,100);
+      p0 = zeros(1,N); p0(1)=1;
+      L = ctmc_exps(Q,tt,p0);
+      plot( tt, L(:,1), ";State 1;", "linewidth", 2, \
+            tt, L(:,2), ";State 2;", "linewidth", 2, \
+            tt, L(:,3), ";State 3;", "linewidth", 2, \
+            tt, L(:,4), ";State 4 (absorbing);", "linewidth", 2);
+      legend("location","northwest");
+      xlabel("Time");
+      ylabel("Expected sojourn time");</pre>
+</pre>
+   <div class="node">
+<a name="Time-Averaged-Expected-Sojourn-Time"></a>
+<a name="Time_002dAveraged-Expected-Sojourn-Time"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Expected-Time-to-Absorption">Expected Time to Absorption</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Expected-Sojourn-Time">Expected Sojourn Time</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">4.2.4 Time-Averaged Expected Sojourn Time</h4>
+
+<p><a name="doc_002dctmc_005ftaexps"></a>
+
+<div class="defun">
+&mdash; Function File: <var>M</var> = <b>ctmc_taexps</b> (<var>Q, tt, p</var>)<var><a name="index-ctmc_005ftaexps-23"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-24"></a><a name="index-Time_002dalveraged-sojourn-time-25"></a>
+Compute the <em>time-averaged sojourn time</em> <var>M</var><code>(t,j)</code>,
+defined as the fraction of the time interval <code>[0,</code><var>tt</var><code>(t))</code> spent in
+state j, assuming that at time 0 the state occupancy
+probability was <var>p</var>.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>Q</var><dd>Infinitesimal generator matrix. <var>Q</var><code>(i,j)</code> is the transition
+rate from state i to state j,
+1 &le; i \neq j &le; N. The
+matrix <var>Q</var> must also satisfy the condition <code>sum(</code><var>Q</var><code>,2) == 0</code>
+
+          <br><dt><var>tt</var><dd>This parameter is a vector used for numerical integration of the
+sujourn time. The first element <var>tt</var><code>(1)</code> must be slightly
+larger than 0, and the
+last element <var>tt</var><code>(end)</code> must be the upper limit of the
+interval [0,t) of interest (<var>tt</var><code>(end) == t</code>). 
+This vector is used by the ODE solver to compute the solution
+<var>M</var>.
+
+          <br><dt><var>p</var><dd><var>p</var><code>(i)</code> is the probability that, at time 0, the system was in
+state i, for all i = 1, <small class="dots">...</small>, N
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>M</var><dd><var>M</var><code>(t,j)</code> is the expected fraction of time spent in state
+j during the interval [0,tt(t)) assuming that the state
+occupancy probability at time zero was <var>p</var>. <code>1 &le;
+</code><var>t</var><code> &le; length(</code><var>tt</var><code>)</code>
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+<pre class="example"><pre class="verbatim">      lambda = 0.5;
+      N = 4;
+      birth = lambda*linspace(1,N-1,N-1);
+      death = zeros(1,N-1);
+      Q = diag(birth,1)+diag(death,-1);
+      Q -= diag(sum(Q,2));
+      t = linspace(1e-3,50,500);
+      p = zeros(1,N); p(1)=1;
+      M = ctmc_taexps(Q,t,p);
+      plot(t, M(:,1), ";State 1;", "linewidth", 2, \
+           t, M(:,2), ";State 2;", "linewidth", 2, \
+           t, M(:,3), ";State 3;", "linewidth", 2, \
+           t, M(:,4), ";State 4 (absorbing);", "linewidth", 2 );
+      legend("location","east");
+      xlabel("Time");
+      ylabel("Time-averaged Expected sojourn time");</pre>
+</pre>
+   <div class="node">
+<a name="Expected-Time-to-Absorption"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#CTMC-First-Passage-Times">CTMC First Passage Times</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Time_002dAveraged-Expected-Sojourn-Time">Time-Averaged Expected Sojourn Time</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">4.2.5 Expected Time to Absorption</h4>
+
+<p>If we consider a Markov Chain with absorbing states, it is possible to
+define the <em>expected time to absorption</em> as the expected time
+until the system goes into an absorbing state. More specifically, let
+us suppose that A is the set of transient (i.e., non-absorbing)
+states of a CTMC with N states and infinitesimal generator
+matrix \bf Q. The expected time to absorption \bf
+L_A(\infty) is defined as the solution of the following equation:
+
+<pre class="example">     L_A( inf ) Q_A = -pi_A(0)
+</pre>
+   <p class="noindent">where \bf Q_A is the restriction of matrix \bf Q to
+only states in A, and \bf \pi_A(0) is the initial
+state occupancy probability at time 0, restricted to states in
+A.
+
+   <p><a name="doc_002dctmc_005fmtta"></a>
+
+<div class="defun">
+&mdash; Function File: <var>t</var> = <b>ctmc_mtta</b> (<var>Q, p</var>)<var><a name="index-ctmc_005fmtta-26"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-27"></a><a name="index-Mean-time-to-absorption-28"></a>
+Compute the Mean-Time to Absorption (MTTA) starting from initial
+occupancy probability <var>p</var> at time 0. If there are no absorbing
+states, this function fails with an error.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>Q</var><dd>N \times N infinitesimal generator matrix. <var>Q</var><code>(i,j)</code>
+is the transition rate from state i to state j, i
+\neq j. The matrix <var>Q</var> must satisfy the condition
+\sum_j=1^N Q_i j = 0
+
+          <br><dt><var>p</var><dd><var>p</var><code>(i)</code> is the probability that the system is in state i
+at time 0, for each i=1, <small class="dots">...</small>, N
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>t</var><dd>Mean time to absorption of the process represented by matrix <var>Q</var>. 
+If there are no absorbing states, this function fails.
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+   <p>Let us consider a simple model of a redundant disk array. We assume
+that the array is made of 5 independent disks, such that the array can
+tolerate up to 2 disk failures without losing data. If three or more
+disks break, the array is dead and unrecoverable. We want to estimate
+the Mean-Time-To-Failure (MTTF) of the disk array.
+
+   <p>We model this system as a 4 states Markov chain with state space
+\ 2, 3, 4, 5 \. State i denotes the fact that exactly
+i disks are active; state 2 is absorbing. Let \mu
+be the failure rate of a single disk. The system starts in state
+5 (all disks are operational). We use a pure death process,
+with death rate from state i to state i-1 is \mu
+i, for i = 3, 4, 5).
+
+   <p>The MTTF of the disk array is the MTTA of the Markov Chain, and can be
+computed with the following expression:
+
+<pre class="example"><pre class="verbatim">      mu = 0.01;
+      death = [ 3 4 5 ] * mu;
+      Q = diag(death,-1);
+      Q -= diag(sum(Q,2));
+      t = ctmc_mtta(Q,[0 0 0 1])</pre>    &rArr; t = 78.333
+</pre>
+   <p class="noindent"><strong>REFERENCES</strong>
+
+   <p>G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, <cite>Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications</cite>, Wiley,
+1998.
+
+   <p><a name="index-Bolch_002c-G_002e-29"></a><a name="index-Greiner_002c-S_002e-30"></a><a name="index-de-Meer_002c-H_002e-31"></a><a name="index-Trivedi_002c-K_002e-32"></a>
+<div class="node">
+<a name="CTMC-First-Passage-Times"></a>
+<p><hr>
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Expected-Time-to-Absorption">Expected Time to Absorption</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">4.2.6 First Passage Times</h4>
+
+<p><a name="doc_002dctmc_005ffpt"></a>
+
+<div class="defun">
+&mdash; Function File: <var>M</var> = <b>ctmc_fpt</b> (<var>Q</var>)<var><a name="index-ctmc_005ffpt-33"></a></var><br>
+&mdash; Function File: <var>m</var> = <b>ctmc_fpt</b> (<var>Q, i, j</var>)<var><a name="index-ctmc_005ffpt-34"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-35"></a><a name="index-First-passage-times-36"></a>
+If called with a single argument, computes the mean first passage
+times <var>M</var><code>(i,j)</code>, the average times before state <var>j</var> is
+reached, starting from state <var>i</var>, for all 1 \leq i, j \leq
+N. If called with three arguments, returns the single value
+<var>m</var><code> = </code><var>M</var><code>(i,j)</code>.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>Q</var><dd>Infinitesimal generator matrix. <var>Q</var> is a N \times N square
+matrix where <var>Q</var><code>(i,j)</code> is the transition rate from state
+i to state j, for 1 &le; i \neq j &le; N. 
+Transition rates must be nonnegative, and \sum_j=1^N Q_i j = 0
+
+          <br><dt><var>i</var><dd>Initial state.
+
+          <br><dt><var>j</var><dd>Destination state. If <var>j</var> is a vector, returns the mean first passage
+time to any state in <var>j</var>.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>M</var><dd>If this function is called with a single argument, the result
+<var>M</var><code>(i,j)</code> is the average time before state
+<var>j</var> is visited for the first time, starting from state <var>i</var>.
+
+          <br><dt><var>m</var><dd>If this function is called with three arguments, the result
+<var>m</var> is the average time before state <var>j</var> is visited for the first
+time, starting from state <var>i</var>.
+
+        </dl>
+
+        </blockquote></div>
+
+<!-- DO NOT EDIT!  Generated automatically by munge-texi. -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla -->
+<!-- This file is part of the queueing toolbox, a Queueing Networks -->
+<!-- analysis package for GNU Octave. -->
+<!-- The queueing toolbox 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 3 of -->
+<!-- the License, or (at your option) any later version. -->
+<!-- The queueing toolbox 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. -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with the queueing toolbox; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Single-Station-Queueing-Systems"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Queueing-Networks">Queueing Networks</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Markov-Chains">Markov Chains</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">5 Single Station Queueing Systems</h2>
+
+<p>Single Station Queueing Systems contain a single station, and are thus
+quite easy to analyze. The <code>queueing</code> package contains functions
+for handling the following types of queues:
+
+<ul class="menu">
+<li><a accesskey="1" href="#The-M_002fM_002f1-System">The M/M/1 System</a>:     Single-server queueing station. 
+<li><a accesskey="2" href="#The-M_002fM_002fm-System">The M/M/m System</a>:     Multiple-server queueing station. 
+<li><a accesskey="3" href="#The-M_002fM_002finf-System">The M/M/inf System</a>:   Infinite-server (delay center) station. 
+<li><a accesskey="4" href="#The-M_002fM_002f1_002fK-System">The M/M/1/K System</a>:   Single-server, finite-capacity queueing station. 
+<li><a accesskey="5" href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a>:   Multiple-server, finite-capacity queueing station. 
+<li><a accesskey="6" href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a>:   Asymmetric multiple-server queueing station. 
+<li><a accesskey="7" href="#The-M_002fG_002f1-System">The M/G/1 System</a>:  Single-server with general service time distribution. 
+<li><a accesskey="8" href="#The-M_002fHm_002f1-System">The M/Hm/1 System</a>:  Single-server with hyperexponential service time distribution. 
+</ul>
+
+   <p>The functions which analyze the queues above can be used as building
+blocks for analyzing Queueing Networks. For example, Jackson networks
+can be solved by computing the aggregate arrival rates to each node,
+and then solving each node in isolation as if it were a single station
+queueing system.
+
+<!-- M/M/1 -->
+<div class="node">
+<a name="The-M%2fM%2f1-System"></a>
+<a name="The-M_002fM_002f1-System"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#The-M_002fM_002fm-System">The M/M/m System</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">5.1 The M/M/1 System</h3>
+
+<p>The M/M/1 system is made of a single server connected to an
+unlimited FCFS queue. The mean arrival rate is Poisson with arrival
+rate \lambda; the service time is exponentially distributed
+with average service rate \mu. The system is stable if
+\lambda &lt; \mu.
+
+   <p><a name="doc_002dqnmm1"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>] = <b>qnmm1</b> (<var>lambda, mu</var>)<var><a name="index-qnmm1-37"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fM_002f1_007d-system-38"></a>
+Compute utilization, response time, average number of requests
+and throughput for a M/M/1 queue.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate (<var>lambda</var><code> &gt; 0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code> &gt; </code><var>lambda</var>).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>Server utilization
+
+          <br><dt><var>R</var><dd>Service center response time
+
+          <br><dt><var>Q</var><dd>Average number of requests in the system
+
+          <br><dt><var>X</var><dd>Service center throughput. If the system is ergodic,
+we will always have <var>X</var><code> = </code><var>lambda</var>
+
+          <br><dt><var>p0</var><dd>Steady-state probability that there are no requests in the system.
+
+        </dl>
+
+        <p><var>lambda</var> and <var>mu</var> can be vectors of the same size. In this
+case, the results will be vectors as well.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnmmm, qnmminf, qnmmmk.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">G. Bolch, S. Greiner, H. de Meer and K. Trivedi, <cite>Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications</cite>, Wiley, 1998, Section 6.3.
+
+   <p><a name="index-Bolch_002c-G_002e-39"></a><a name="index-Greiner_002c-S_002e-40"></a><a name="index-de-Meer_002c-H_002e-41"></a><a name="index-Trivedi_002c-K_002e-42"></a>
+<!-- M/M/m -->
+<div class="node">
+<a name="The-M%2fM%2fm-System"></a>
+<a name="The-M_002fM_002fm-System"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#The-M_002fM_002finf-System">The M/M/inf System</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#The-M_002fM_002f1-System">The M/M/1 System</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">5.2 The M/M/m System</h3>
+
+<p>The M/M/m system is similar to the M/M/1 system, except
+that there are m \geq 1 identical servers connected to a single
+queue. Thus, at most m requests can be served at the same
+time. The M/M/m system can be seen as a single server with
+load-dependent service rate \mu(n), which is a function of the
+number n of nodes in the center:
+
+<pre class="example">     <code>mu(n) = min(m,n)*mu</code>
+</pre>
+   <p><a name="doc_002dqnmmm"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>, <var>pm</var>] = <b>qnmmm</b> (<var>lambda, mu</var>)<var><a name="index-qnmmm-43"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>, <var>pm</var>] = <b>qnmmm</b> (<var>lambda, mu, m</var>)<var><a name="index-qnmmm-44"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fM_002fm_007d-system-45"></a>
+Compute utilization, response time, average number of requests in
+service and throughput for a M/M/m queue, a queueing
+system with m identical service centers connected to a single queue.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate (<var>lambda</var><code>&gt;0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code>&gt;</code><var>lambda</var>).
+
+          <br><dt><var>m</var><dd>Number of servers (<var>m</var><code> &ge; 1</code>). 
+If omitted, it is assumed <var>m</var><code>=1</code>.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>Service center utilization, U = \lambda / (m \mu).
+
+          <br><dt><var>R</var><dd>Service center response time
+
+          <br><dt><var>Q</var><dd>Average number of requests in the system
+
+          <br><dt><var>X</var><dd>Service center throughput. If the system is ergodic,
+we will always have <var>X</var><code> = </code><var>lambda</var>
+
+          <br><dt><var>p0</var><dd>Steady-state probability that there are 0 requests in the system
+
+          <br><dt><var>pm</var><dd>Steady-state probability that an arriving request has to wait in the
+queue
+
+        </dl>
+
+        <p><var>lambda</var>, <var>mu</var> and <var>m</var> can be vectors of the same size. In this
+case, the results will be vectors as well.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnmm1,qnmminf,qnmmmk.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">G. Bolch, S. Greiner, H. de Meer and K. Trivedi, <cite>Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications</cite>, Wiley, 1998, Section 6.5.
+
+   <p><a name="index-Bolch_002c-G_002e-46"></a><a name="index-Greiner_002c-S_002e-47"></a><a name="index-de-Meer_002c-H_002e-48"></a><a name="index-Trivedi_002c-K_002e-49"></a>
+<!-- M/M/inf -->
+<div class="node">
+<a name="The-M%2fM%2finf-System"></a>
+<a name="The-M_002fM_002finf-System"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#The-M_002fM_002f1_002fK-System">The M/M/1/K System</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#The-M_002fM_002fm-System">The M/M/m System</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">5.3 The M/M/inf System</h3>
+
+<p>The M/M/\infty system is similar to the M/M/m system,
+except that there are infinitely many identical servers (that is,
+m = \infty). Each new request is assigned to a new server, so
+that queueing never occurs. The M/M/\infty system is always
+stable.
+
+   <p><a name="doc_002dqnmminf"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>] = <b>qnmminf</b> (<var>lambda, mu</var>)<var><a name="index-qnmminf-50"></a></var><br>
+<blockquote>
+        <p>Compute utilization, response time, average number of requests and
+throughput for a M/M/\infty queue. This is a system with an
+infinite number of identical servers. Note that a M/M/\infty
+system is always stable, regardless the values of the arrival and
+service rates.
+
+        <p><a name="index-g_t_0040math_007bM_002fM_002f_007dinf-system-51"></a>
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate (<var>lambda</var><code>&gt;0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code>&gt;0</code>).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>Traffic intensity (defined as \lambda/\mu). Note that this is
+different from the utilization, which in the case of M/M/\infty
+centers is always zero.
+
+          <p><a name="index-traffic-intensity-52"></a>
+<br><dt><var>R</var><dd>Service center response time.
+
+          <br><dt><var>Q</var><dd>Average number of requests in the system (which is equal to the
+traffic intensity \lambda/\mu).
+
+          <br><dt><var>X</var><dd>Throughput (which is always equal to <var>X</var><code> = </code><var>lambda</var>).
+
+          <br><dt><var>p0</var><dd>Steady-state probability that there are no requests in the system
+
+        </dl>
+
+        <p><var>lambda</var> and <var>mu</var> can be vectors of the same size. In this
+case, the results will be vectors as well.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnmm1,qnmmm,qnmmmk.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">G. Bolch, S. Greiner, H. de Meer and K. Trivedi, <cite>Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications</cite>, Wiley, 1998, Section 6.4.
+
+   <p><a name="index-Bolch_002c-G_002e-53"></a><a name="index-Greiner_002c-S_002e-54"></a><a name="index-de-Meer_002c-H_002e-55"></a><a name="index-Trivedi_002c-K_002e-56"></a>
+<!-- M/M/1/k -->
+<div class="node">
+<a name="The-M%2fM%2f1%2fK-System"></a>
+<a name="The-M_002fM_002f1_002fK-System"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#The-M_002fM_002finf-System">The M/M/inf System</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">5.4 The M/M/1/K System</h3>
+
+<p>In a M/M/1/K finite capacity system there can be at most
+k jobs at any time. If a new request tries to join the system
+when there are already K other requests, the arriving request
+is lost. The queue has K-1 slots. The M/M/1/K system is
+always stable, regardless of the arrival and service rates
+\lambda and \mu.
+
+   <p><a name="doc_002dqnmm1k"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>, <var>pK</var>] = <b>qnmm1k</b> (<var>lambda, mu, K</var>)<var><a name="index-qnmm1k-57"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fM_002f1_002fK_007d-system-58"></a>
+Compute utilization, response time, average number of requests and
+throughput for a M/M/1/K finite capacity system. In a
+M/M/1/K queue there is a single server; the maximum number of
+requests in the system is K, and the maximum queue length is
+K-1.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate (<var>lambda</var><code>&gt;0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code>&gt;0</code>).
+
+          <br><dt><var>K</var><dd>Maximum number of requests allowed in the system (<var>K</var><code> &ge; 1</code>).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>Service center utilization, which is defined as <var>U</var><code> = 1-</code><var>p0</var>
+
+          <br><dt><var>R</var><dd>Service center response time
+
+          <br><dt><var>Q</var><dd>Average number of requests in the system
+
+          <br><dt><var>X</var><dd>Service center throughput
+
+          <br><dt><var>p0</var><dd>Steady-state probability that there are no requests in the system
+
+          <br><dt><var>pK</var><dd>Steady-state probability that there are K requests in the system
+(i.e., that the system is full)
+
+        </dl>
+
+        <p><var>lambda</var>, <var>mu</var> and <var>K</var> can be vectors of the
+same size. In this case, the results will be vectors as well.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnmm1,qnmminf,qnmmm.
+
+        </blockquote></div>
+
+<!-- M/M/m/k -->
+<div class="node">
+<a name="The-M%2fM%2fm%2fK-System"></a>
+<a name="The-M_002fM_002fm_002fK-System"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#The-M_002fM_002f1_002fK-System">The M/M/1/K System</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">5.5 The M/M/m/K System</h3>
+
+<p>The M/M/m/K finite capacity system is similar to the
+M/M/1/k system except that the number of servers is m,
+where 1 \leq m \leq K. The queue is made of K-m
+slots. The M/M/m/K system is always stable.
+
+   <p><a name="doc_002dqnmmmk"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>, <var>pK</var>] = <b>qnmmmk</b> (<var>lambda, mu, m, K</var>)<var><a name="index-qnmmmk-59"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fM_002fm_002fK_007d-system-60"></a>
+Compute utilization, response time, average number of requests and
+throughput for a M/M/m/K finite capacity system. In a
+M/M/m/K system there are m \geq 1 identical service
+centers sharing a fixed-capacity queue. At any time, at most K &ge; m requests can be in the system. The maximum queue length
+is K-m. This function generates and
+solves the underlying CTMC.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate (<var>lambda</var><code>&gt;0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code>&gt;0</code>).
+
+          <br><dt><var>m</var><dd>Number of servers (<var>m</var><code> &ge; 1</code>).
+
+          <br><dt><var>K</var><dd>Maximum number of requests allowed in the system,
+including those inside the service centers
+(<var>K</var><code> &ge; </code><var>m</var>).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>Service center utilization
+
+          <br><dt><var>R</var><dd>Service center response time
+
+          <br><dt><var>Q</var><dd>Average number of requests in the system
+
+          <br><dt><var>X</var><dd>Service center throughput
+
+          <br><dt><var>p0</var><dd>Steady-state probability that there are no requests in the system.
+
+          <br><dt><var>pK</var><dd>Steady-state probability that there are <var>K</var> requests in the system
+(i.e., probability that the system is full).
+
+        </dl>
+
+        <p><var>lambda</var>, <var>mu</var>, <var>m</var> and <var>K</var> can be either scalars, or
+vectors of the  same size. In this case, the results will be vectors
+as well.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnmm1,qnmminf,qnmmm.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">G. Bolch, S. Greiner, H. de Meer and K. Trivedi, <cite>Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications</cite>, Wiley, 1998, Section 6.6.
+
+   <p><a name="index-Bolch_002c-G_002e-61"></a><a name="index-Greiner_002c-S_002e-62"></a><a name="index-de-Meer_002c-H_002e-63"></a><a name="index-Trivedi_002c-K_002e-64"></a>
+
+<!-- Approximate M/M/m -->
+<div class="node">
+<a name="The-Asymmetric-M%2fM%2fm-System"></a>
+<a name="The-Asymmetric-M_002fM_002fm-System"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#The-M_002fG_002f1-System">The M/G/1 System</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">5.6 The Asymmetric M/M/m System</h3>
+
+<p>The Asymmetric M/M/m system contains m servers connected
+to a single queue. Differently from the M/M/m system, in the
+asymmetric M/M/m each server may have a different service time.
+
+   <p><a name="doc_002dqnammm"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnammm</b> (<var>lambda, mu</var>)<var><a name="index-qnammm-65"></a></var><br>
+<blockquote>
+        <p><a name="index-Asymmetric-_0040math_007bM_002fM_002fm_007d-system-66"></a>
+Compute <em>approximate</em> utilization, response time, average number
+of requests in service and throughput for an asymmetric  M/M/m
+queue. In this system there are m different service centers
+connected to a single queue. Each server has its own (possibly different)
+service rate. If there is more than one server available, requests
+are routed to a randomly-chosen one.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate (<var>lambda</var><code>&gt;0</code>).
+
+          <br><dt><var>mu</var><dd><var>mu</var><code>(i)</code> is the service rate of server
+i, 1 &le; i &le; m. 
+The system must be ergodic (<var>lambda</var><code> &lt; sum(</code><var>mu</var><code>)</code>).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>Approximate service center utilization,
+U = \lambda / ( \sum_i \mu_i ).
+
+          <br><dt><var>R</var><dd>Approximate service center response time
+
+          <br><dt><var>Q</var><dd>Approximate number of requests in the system
+
+          <br><dt><var>X</var><dd>Approximate service center throughput. If the system is ergodic,
+we will always have <var>X</var><code> = </code><var>lambda</var>
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnmmm.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">G. Bolch, S. Greiner, H. de Meer and K. Trivedi, <cite>Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications</cite>, Wiley, 1998
+
+   <p><a name="index-Bolch_002c-G_002e-67"></a><a name="index-Greiner_002c-S_002e-68"></a><a name="index-de-Meer_002c-H_002e-69"></a><a name="index-Trivedi_002c-K_002e-70"></a>
+<div class="node">
+<a name="The-M%2fG%2f1-System"></a>
+<a name="The-M_002fG_002f1-System"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#The-M_002fHm_002f1-System">The M/Hm/1 System</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">5.7 The M/G/1 System</h3>
+
+<p><a name="doc_002dqnmg1"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>] = <b>qnmg1</b> (<var>lambda, xavg, x2nd</var>)<var><a name="index-qnmg1-71"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fG_002f1_007d-system-72"></a>
+Compute utilization, response time, average number of requests and
+throughput for a M/G/1 system. The service time distribution
+is described by its mean <var>xavg</var>, and by its second moment
+<var>x2nd</var>. The computations are based on results from L. Kleinrock,
+<cite>Queuing Systems</cite>, Wiley, Vol 2, and Pollaczek-Khinchine formula.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate.
+
+          <br><dt><var>xavg</var><dd>Average service time
+
+          <br><dt><var>x2nd</var><dd>Second moment of service time distribution
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>Service center utilization
+
+          <br><dt><var>R</var><dd>Service center response time
+
+          <br><dt><var>Q</var><dd>Average number of requests in the system
+
+          <br><dt><var>X</var><dd>Service center throughput
+
+          <br><dt><var>p0</var><dd>probability that there is not any request at system
+
+        </dl>
+
+        <p><var>lambda</var>, <var>xavg</var>, <var>t2nd</var> can be vectors of the
+same size. In this case, the results will be vectors as well.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnmh1.
+
+        </blockquote></div>
+
+<div class="node">
+<a name="The-M%2fHm%2f1-System"></a>
+<a name="The-M_002fHm_002f1-System"></a>
+<p><hr>
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#The-M_002fG_002f1-System">The M/G/1 System</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">5.8 The M/H_m/1 System</h3>
+
+<p><a name="doc_002dqnmh1"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>] = <b>qnmh1</b> (<var>lambda, mu, alpha</var>)<var><a name="index-qnmh1-73"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fH_005fm_002f1_007d-system-74"></a>
+Compute utilization, response time, average number of requests and
+throughput for a M/H_m/1 system. In this system, the customer
+service times have hyper-exponential distribution:
+
+     <pre class="example">                 ___ m
+                 \
+          B(x) =  &gt;  alpha(j) * (1-exp(-mu(j)*x))   x&gt;0
+                 /__
+                     j=1
+</pre>
+        <p>where \alpha_j is the probability that the request is served
+at phase j, in which case the average service rate is
+\mu_j. After completing service at phase j, for
+some j, the request exits the system.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate.
+
+          <br><dt><var>mu</var><dd><var>mu</var><code>(j)</code> is the phase j service rate. The total
+number of phases m is <code>length(</code><var>mu</var><code>)</code>.
+
+          <br><dt><var>alpha</var><dd><var>alpha</var><code>(j)</code> is the probability that a request
+is served at phase j. <var>alpha</var> must have the same size
+as <var>mu</var>.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>Service center utilization
+
+          <br><dt><var>R</var><dd>Service center response time
+
+          <br><dt><var>Q</var><dd>Average number of requests in the system
+
+          <br><dt><var>X</var><dd>Service center throughput
+
+        </dl>
+
+     <!-- @seealso{qnmhr1} -->
+        </blockquote></div>
+
+<!-- DO NOT EDIT!  Generated automatically by munge-texi. -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla -->
+<!-- This file is part of the queueing toolbox, a Queueing Networks -->
+<!-- analysis package for GNU Octave. -->
+<!-- The queueing toolbox 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 3 of -->
+<!-- the License, or (at your option) any later version. -->
+<!-- The queueing toolbox 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. -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with the queueing toolbox; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Queueing-Networks"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Contributing-Guidelines">Contributing Guidelines</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">6 Queueing Networks</h2>
+
+<ul class="menu">
+<li><a accesskey="1" href="#Introduction-to-QNs">Introduction to QNs</a>:                  A brief introduction to Queueing Networks. 
+<li><a accesskey="2" href="#Generic-Algorithms">Generic Algorithms</a>:                   High-level functions for QN analysis
+<li><a accesskey="3" href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a>:      Functions to analyze product-form QNs
+<li><a accesskey="4" href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a>:  Functions to analyze non product-form QNs
+<li><a accesskey="5" href="#Bounds-on-performance">Bounds on performance</a>:                Functions to compute performance bounds
+<li><a accesskey="6" href="#Utility-functions">Utility functions</a>:                    Utility functions to compute miscellaneous quantities
+</ul>
+
+<p><a name="index-queueing-networks-75"></a>
+<!-- INTRODUCTION -->
+<div class="node">
+<a name="Introduction-to-QNs"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Generic-Algorithms">Generic Algorithms</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">6.1 Introduction to QNs</h3>
+
+<p>Queueing Networks (QN) are a very simple yet powerful modeling tool
+which is used to analyze many kind of systems. In its simplest form, a
+QN is made of K service centers. Each service center i
+has a queue, which is connected to m_i (generally identical)
+<em>servers</em>. Customers (or requests) arrive at the service center,
+and join the queue if there is a slot available. Then, requests are
+served according to a (de)queueing policy. After service completes,
+the requests leave the service center.
+
+   <p>The service centers for which m_i = \infty are called
+<em>delay centers</em> or <em>infinite servers</em>. If a service center
+has infinite servers, of course each new request will find one server
+available, so there will never be queueing.
+
+   <p>Requests join the queue according to a <em>queueing policy</em>, such as:
+
+     <dl>
+<dt><strong>FCFS</strong><dd>First-Come-First-Served
+
+     <br><dt><strong>LCFS-PR</strong><dd>Last-Come-First-Served, Preemptive Resume
+
+     <br><dt><strong>PS</strong><dd>Processor Sharing
+
+     <br><dt><strong>IS</strong><dd>Infinite Server, there is an infinite number of identical servers so
+that each request always finds a server available, and there is no
+queueing
+
+   </dl>
+
+   <p>A population of <em>requests</em> or <em>customers</em> arrives to the
+system system, requesting service to the service centers.  The request
+population may be <em>open</em> or <em>closed</em>. In open systems there
+is an infinite population of requests. New customers arrive from
+outside the system, and eventually leave the system. In closed systems
+there is a fixed population of request which continuously interacts
+with the system.
+
+   <p>There might be a single class of requests, meaning that all requests
+behave in the same way (e.g., they spend the same average time on each
+particular server), or there might be multiple classes of requests.
+
+<h4 class="subsection">6.1.1 Single class models</h4>
+
+<p>In single class models, all requests are indistinguishable and belong to
+the same class. This means that every request has the same average
+service time, and all requests move through the system with the same
+routing probabilities.
+
+<p class="noindent"><strong>Model Inputs</strong>
+
+     <dl>
+<dt>\lambda_i<dd>External arrival rate to service center i.
+
+     <br><dt>\lambda<dd>Overall external arrival rate to the whole system: \lambda =
+\sum_i \lambda_i.
+
+     <br><dt>S_i<dd>Average service time. S_i is the average service time on service
+center i. In other words, S_i is the average time from the
+instant in which a request is extracted from the queue and starts being
+service, and the instant at which service finishes and the request moves
+to another queue (or exits the system).
+
+     <br><dt>P_ij<dd>Routing probability matrix. \bf P = P_ij is a K \times
+K matrix such that P_ij is the probability that a request
+completing service at server i will move directly to server
+j, The probability that a request leaves the system after service
+at service center i is 1-\sum_j=1^K P_ij.
+
+     <br><dt>V_i<dd>Average number of visits. V_i is the average number of visits to
+the service center i. This quantity will be described shortly.
+
+   </dl>
+
+<p class="noindent"><strong>Model Outputs</strong>
+
+     <dl>
+<dt>U_i<dd>Service center utilization. U_i is the utilization of service
+center i. The utilization is defined as the fraction of time in
+which the resource is busy (i.e., the server is processing requests).
+
+     <br><dt>R_i<dd>Average response time. R_i is the average response time of
+service center i. The average response time is defined as the
+average time between the arrival of a customer in the queue, and the
+completion of service.
+
+     <br><dt>Q_i<dd>Average number of customers. Q_i is the average number of
+requests in service center i. This includes both the requests in
+the queue, and the request being served.
+
+     <br><dt>X_i<dd>Throughput. X_i is the throughput of service center i. 
+The throughput is defined as the ratio of job completions (i.e., average
+number of jobs completed over a fixed interval of time).
+
+   </dl>
+
+<p class="noindent">Given these output parameters, additional performance measures can
+be computed as follows:
+
+     <dl>
+<dt>X<dd>System throughput, X = X_1 / V_1
+
+     <br><dt>R<dd>System response time, R = \sum_k=1^K R_k V_k
+
+     <br><dt>Q<dd>Average number of requests in the system, Q = N-XZ
+
+   </dl>
+
+   <p>For open, single-class models, the scalar \lambda denotes the
+external arrival rate of requests to the system. The average number of
+visits satisfy the following equation:
+
+<pre class="example">     V == P0 + V*P;
+</pre>
+   <p class="noindent">where P_0 j is the probability that an external
+arrival goes to service center j. If \lambda_j is the
+external arrival rate to service center j, and \lambda =
+\sum_j \lambda_j is the overall external arrival rate, then
+P_0 j = \lambda_j / \lambda.
+
+   <p>For closed models, the visit ratios satisfy the following equation:
+
+<pre class="example">     V(1) == 1 &amp;&amp; V == V*P;
+</pre>
+   <h4 class="subsection">6.1.2 Multiple class models</h4>
+
+<p>In multiple class QN models, we assume that there exist C
+different classes of requests. Each request from class c spends
+on average time S_ck in service at service center k. For
+open models, we denote with \bf \lambda = \lambda_ck the
+arrival rates, where \lambda_ck is the external arrival rate of
+class c customers at service center k. For closed models,
+we denote with \bf N = (N_1, N_2, \ldots N_C) the population
+vector, where N_c is the number of class c requests in the
+system.
+
+   <p>The transition probability matrix for these kind of networks will be a
+C \times K \times C \times K matrix \bf P =
+P_risj such that P_risj is the probability that a
+class r request which completes service at center i will
+join server j as a class s request.
+
+   <p>Model input and outputs can be adjusted by adding additional
+indexes for the customer classes.
+
+<p class="noindent"><strong>Model Inputs</strong>
+
+     <dl>
+<dt>\lambda_ci<dd>External arrival rate of class-c requests to service center i
+
+     <br><dt>\lambda<dd>Overall external arrival rate to the whole system: \lambda = \sum_c \sum_i \lambda_ci
+
+     <br><dt>S_ci<dd>Average service time. S_ci is the average service time on service
+center i for class c requests.
+
+     <br><dt>P_risj<dd>Routing probability matrix. \bf P = P_risj is a C
+\times K \times C \times K matrix such that P_risj is the
+probability that a class r request which completes service at
+server i will move to server j as a class s
+request.
+
+     <br><dt>V_ci<dd>Average number of visits. V_ci is the average number of visits
+of class c requests to the service center i.
+
+   </dl>
+
+<p class="noindent"><strong>Model Outputs</strong>
+
+     <dl>
+<dt>U_ci<dd>Utilization of service center i by class c requests. The
+utilization is defined as the fraction of time in which the resource is
+busy (i.e., the server is processing requests).
+
+     <br><dt>R_ci<dd>Average response time experienced by class c requests on service
+center i. The average response time is defined as the average
+time between the arrival of a customer in the queue, and the completion
+of service.
+
+     <br><dt>Q_ci<dd>Average number of class c requests on service center
+i. This includes both the requests in the queue, and the request
+being served.
+
+     <br><dt>X_ci<dd>Throughput of service center i for class c requests.  The
+throughput is defined as the rate of completion of class c
+requests.
+
+   </dl>
+
+<p class="noindent">It is possible to define aggregate performance measures as follows:
+
+     <dl>
+<dt>U_i<dd>Utilization of service center i:
+<code>Ui = sum(U,1);</code>
+
+     <br><dt>R_c<dd>System response time for class c requests:
+<code>Rc = sum( V.*R, 1 );</code>
+
+     <br><dt>Q_c<dd>Average number of class c requests in the system:
+<code>Qc = sum( Q, 2 );</code>
+
+     <br><dt>X_c<dd>Class c throughput:
+<code>Xc = X(:,1) ./ V(:,1);</code>
+
+   </dl>
+
+   <p>We can define the visit ratios V_sj for class s
+customers at service center j as follows:
+
+   <p>V_sj = sum_r sum_i V_ri P_risj, for all s,j
+
+<p class="noindent">while for open networks:
+
+   <p>V_sj = P_0sj + sum_r sum_i V_ri P_risj, for all s,j
+
+<p class="noindent">where P_0sj is the probability that an external
+arrival goes to service center j as a class-s request. 
+If \lambda_sj is the external arrival rate of class s
+requests to service center j, and \lambda = \sum_s \sum_j
+\lambda_sj is the overall external arrival rate to the whole system,
+then P_0sj = \lambda_sj / \lambda.
+
+<div class="node">
+<a name="Generic-Algorithms"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Introduction-to-QNs">Introduction to QNs</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">6.2 Generic Algorithms</h3>
+
+<p>The <code>queueing</code> package provides a couple of high-level functions
+for defining and solving QN models. These functions can be used to
+define a open or closed QN model (with single or multiple job
+classes), with arbitrary configuration and queueing disciplines. At
+the moment only product-form networks can be solved, See <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a>.
+
+   <p>The network is defined by two parameters. The first one is the list of
+nodes, encoded as an Octave <em>cell array</em>. The second parameter is
+the visit ration <var>V</var>, which can be either a vector (for
+single-class models) or a two-dimensional matrix (for multiple-class
+models).
+
+   <p>Individual nodes in the network are structures build using the
+<code>qnmknode</code> function.
+
+   <p><a name="doc_002dqnmknode"></a>
+
+<div class="defun">
+&mdash; Function File: <var>Q</var> = <b>qnmknode</b> (<var>"m/m/m-fcfs", S</var>)<var><a name="index-qnmknode-76"></a></var><br>
+&mdash; Function File: <var>Q</var> = <b>qnmknode</b> (<var>"m/m/m-fcfs", S, m</var>)<var><a name="index-qnmknode-77"></a></var><br>
+&mdash; Function File: <var>Q</var> = <b>qnmknode</b> (<var>"m/m/1-lcfs-pr", S</var>)<var><a name="index-qnmknode-78"></a></var><br>
+&mdash; Function File: <var>Q</var> = <b>qnmknode</b> (<var>"-/g/1-ps", S</var>)<var><a name="index-qnmknode-79"></a></var><br>
+&mdash; Function File: <var>Q</var> = <b>qnmknode</b> (<var>"-/g/1-ps", S, s2</var>)<var><a name="index-qnmknode-80"></a></var><br>
+&mdash; Function File: <var>Q</var> = <b>qnmknode</b> (<var>"-/g/inf", S</var>)<var><a name="index-qnmknode-81"></a></var><br>
+&mdash; Function File: <var>Q</var> = <b>qnmknode</b> (<var>"-/g/inf", S, s2</var>)<var><a name="index-qnmknode-82"></a></var><br>
+<blockquote>
+        <p>Creates a node; this function can be used together with
+<code>qnsolve</code>. It is possible to create either single-class nodes
+(where there is only one customer class), or multiple-class nodes
+(where the service time is given per-class). Furthermore, it is
+possible to specify load-dependent service times.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>S</var><dd>Average service time. S can be either a scalar, a row vector,
+a column vector or a two-dimensional matrix.
+
+               <ul>
+<li>If S is a scalar,
+it is assumed to be a load-independent, class-independent service time.
+
+               <li>If S is a column vector, then <var>S</var><code>(c)</code> is assumed to
+the the load-independent service time for class c customers.
+
+               <li>If S is a row vector, then <var>S</var><code>(n)</code> is assumed to be
+the class-independent service time at the node, when there are n
+requests.
+
+               <li>Finally, if <var>S</var> is a two-dimensional matrix, then
+<var>S</var><code>(c,n)</code> is assumed to be the class c service time
+when there are n requests at the node.
+
+          </ul>
+
+          <br><dt><var>m</var><dd>Number of identical servers at the node. Default is <var>m</var><code>=1</code>.
+
+          <br><dt><var>s2</var><dd>Squared coefficient of variation for the service time. Default is 1.0.
+
+        </dl>
+
+        <p>The returned struct <var>Q</var> should be considered opaque to the client.
+
+     <!-- The returned struct @var{Q} has the following fields: -->
+     <!-- @table @var -->
+     <!-- @item Q.node -->
+     <!-- (String) type of the node; valid values are @code{"m/m/m-fcfs"}, -->
+     <!-- @code{"-/g/1-lcfs-pr"}, @code{"-/g/1-ps"} (Processor-Sharing) -->
+     <!-- and @code{"-/g/inf"} (Infinite Server, or delay center). -->
+     <!-- @item Q.S -->
+     <!-- Average service time. If @code{@var{Q}.S} is a vector, then -->
+     <!-- @code{@var{Q}.S(i)} is the average service time at that node -->
+     <!-- if there are @math{i} requests. -->
+     <!-- @item Q.m -->
+     <!-- Number of identical servers at a @code{"m/m/m-fcfs"}. Default is 1. -->
+     <!-- @item Q.c -->
+     <!-- Number of customer classes. Default is 1. -->
+     <!-- @end table -->
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnsolve.
+
+        </blockquote></div>
+
+   <p>After the network has been defined, it is possible to solve it using
+the <code>qnsolve</code> function. Note that this function is somewhat less
+efficient than those described in later sections, but
+generally easier to use.
+
+   <p><a name="doc_002dqnsolve"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnsolve</b> (<var>"closed", N, QQ, V</var>)<var><a name="index-qnsolve-83"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnsolve</b> (<var>"closed", N, QQ, V, Z</var>)<var><a name="index-qnsolve-84"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnsolve</b> (<var>"open", lambda, QQ, V</var>)<var><a name="index-qnsolve-85"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnsolve</b> (<var>"mixed", lambda, N, QQ, V</var>)<var><a name="index-qnsolve-86"></a></var><br>
+<blockquote>
+        <p>General evaluator of QN models. Networks can be open,
+closed or mixed; single as well as multiclass networks are supported.
+
+          <ul>
+<li>For <strong>closed</strong> networks, the following server types are
+supported: M/M/m&ndash;FCFS, -/G/\infty, -/G/1&ndash;LCFS-PR,
+-/G/1&ndash;PS and load-dependent variants.
+
+          <li>For <strong>open</strong> networks, the following server types are supported:
+M/M/m&ndash;FCFS, -/G/\infty and -/G/1&ndash;PS. General
+load-dependent nodes are <em>not</em> supported. Multiclass open networks
+do not support multiple server M/M/m nodes, but only
+single server M/M/1&ndash;FCFS.
+
+          <li>For <strong>mixed</strong> networks, the following server types are supported:
+M/M/1&ndash;FCFS, -/G/\infty and -/G/1&ndash;PS. General
+load-dependent nodes are <em>not</em> supported.
+
+        </ul>
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Number of requests in the system for closed networks. For
+single-class networks, <var>N</var> must be a scalar. For multiclass
+networks, <var>N</var><code>(c)</code> is the population size of closed class
+c.
+
+          <br><dt><var>lambda</var><dd>External arrival rate (scalar) for open networks. For single-class
+networks, <var>lambda</var> must be a scalar. For multiclass networks,
+<var>lambda</var><code>(c)</code> is the class c overall arrival rate.
+
+          <br><dt><var>QQ</var><dd>List of queues in the network. This must be a cell array
+with N elements, such that <var>QQ</var><code>{i}</code> is
+a struct produced by the <code>qnmknode</code> function.
+
+          <br><dt><var>Z</var><dd>External delay ("think time") for closed networks. Default 0.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>If i is a FCFS node, then <var>U</var><code>(i)</code> is the utilization
+of service center i. If i is an IS node, then
+<var>U</var><code>(i)</code> is the <em>traffic intensity</em> defined as
+<var>X</var><code>(i)*</code><var>S</var><code>(i)</code>.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(i)</code> is the average response time of service center i.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(i)</code> is the average number of customers in service center
+i.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(i)</code> is the throughput of service center i.
+
+        </dl>
+
+        <p>Note that for multiclass networks, the computed results are per-class
+utilization, response time, number of customers and throughput:
+<var>U</var><code>(c,k)</code>, <var>R</var><code>(c,k)</code>, <var>Q</var><code>(c,k)</code>,
+<var>X</var><code>(c,k)</code>,
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+   <p>Let us consider a closed, multiclass network with C=2 classes
+and K=3 service center. Let the population be M=(2, 1)
+(class 1 has 2 requests, and class 2 has 1 request). The nodes are as
+follows:
+
+     <ul>
+<li>Node 1 is a M/M/1&ndash;FCFS node, with load-dependent service
+times. Service times are class-independent, and are defined by the
+matrix <code>[0.2 0.1 0.1; 0.2 0.1 0.1]</code>. Thus, <var>S</var><code>(1,2) =
+0.2</code> means that service time for class 1 customers where there are 2
+requests in 0.2. Note that service times are class-independent;
+
+     <li>Node 2 is a -/G/1&ndash;PS node, with service times
+S_12 = 0.4 for class 1, and S_22 = 0.6 for class 2
+requests;
+
+     <li>Node 3 is a -/G/\infty node (delay center), with service
+times S_13=1 and S_23=2 for class 1 and 2
+respectively.
+
+   </ul>
+
+   <p>After defining the per-class visit count <var>V</var> such that
+<var>V</var><code>(c,k)</code> is the visit count of class c requests to
+service center k.  We can define and solve the model as
+follows:
+
+<pre class="example"><pre class="verbatim">      QQ = { qnmknode( "m/m/m-fcfs", [0.2 0.1 0.1; 0.2 0.1 0.1] ), \
+             qnmknode( "-/g/1-ps", [0.4; 0.6] ), \
+             qnmknode( "-/g/inf", [1; 2] ) };
+      V = [ 1 0.6 0.4; \
+            1 0.3 0.7 ];
+      N = [ 2 1 ];
+      [U R Q X] = qnsolve( "closed", N, QQ, V );</pre></pre>
+   <div class="node">
+<a name="Algorithms-for-Product-Form-QNs"></a>
+<a name="Algorithms-for-Product_002dForm-QNs"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Generic-Algorithms">Generic Algorithms</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">6.3 Algorithms for Product-Form QNs</h3>
+
+<p>Product-form queueing networks fulfill the following assumptions:
+
+     <ul>
+<li>The network can consist of open and closed job classes.
+
+     <li>The following queueing disciplines are allowed: FCFS, PS, LCFS-PR and IS.
+
+     <li>Service times for FCFS nodes must be exponentially distributed and
+class-independent. Service centers at PS, LCFS-PR and IS nodes can
+have any kind of service time distribution with a rational Laplace
+transform.  Furthermore, for PS, LCFS-PR and IS nodes, different
+classes of customers can have different service times.
+
+     <li>The service rate of an FCFS node is only allowed to depend on the
+number of jobs at this node; in a PS, LCFS-PR and IS node the service
+rate for a particular job class can also depend on the number of jobs
+of that class at the node.
+
+     <li>In open networks two kinds of arrival processes are allowed: i) the
+arrival process is Poisson, with arrival rate \lambda which can
+depend on the number of jobs in the network. ii) the arrival process
+consists of U independent Poisson arrival streams where the
+U job sources are assigned to the U chains; the arrival
+rate can be load dependent.
+
+   </ul>
+
+<!-- Jackson Networks -->
+<h4 class="subsection">6.3.1 Jackson Networks</h4>
+
+<p>Jackson networks satisfy the following conditions:
+
+     <ul>
+<li>There is only one job class in the network; the overall number of jobs
+in the system is unlimited.
+
+     <li>There are N service centers in the network. Each service center
+may have Poisson arrivals from outside the system. A job can leave
+the system from any node.
+
+     <li>Arrival rates as well as routing probabilities are independent from
+the number of nodes in the network.
+
+     <li>External arrivals and service times at the service centers are
+exponentially distributed, and in general can be load-dependent.
+
+     <li>Service discipline at each node is FCFS
+
+   </ul>
+
+   <p>We define the <em>joint probability vector</em> \pi(k_1, k_2,
+\ldots k_N) as the steady-state probability that there are k_i
+requests at service center i, for all i=1,2, \ldots N. 
+Jackson networks have the property that the joint probability is the
+product of the marginal probabilities \pi_i:
+
+<pre class="example">     <var>joint_prob</var> = prod( <var>pi</var> )
+</pre>
+   <p class="noindent">where \pi_i(k_i) is the steady-state probability
+that there are k_i requests at service center i.
+
+   <p><a name="doc_002dqnjackson"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnjackson</b> (<var>lambda, S, P </var>)<var><a name="index-qnjackson-87"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnjackson</b> (<var>lambda, S, P, m </var>)<var><a name="index-qnjackson-88"></a></var><br>
+&mdash; Function File: <var>pr</var> = <b>qnjackson</b> (<var>lambda, S, P, m, k</var>)<var><a name="index-qnjackson-89"></a></var><br>
+<blockquote>
+        <p><a name="index-open-network_002c-single-class-90"></a><a name="index-Jackson-network-91"></a>
+With three or four input parameters, this function computes the
+steady-state occupancy probabilities for a Jackson network. With five
+input parameters, this function computes the steady-state probability
+<var>pi</var><code>(j)</code> that there are <var>k</var><code>(j)</code> requests at
+service center j.
+
+        <p>This function solves a subset of Jackson networks, with the
+following constraints:
+
+          <ul>
+<li>External arrival rates are load-independent.
+
+          <li>Service center i consists either of <var>m</var><code>(i) &ge;
+1</code> identical servers with individual average service time
+<var>S</var><code>(i)</code>, or of an Infinite Server (IS) node.
+
+        </ul>
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd><var>lambda</var><code>(i)</code> is
+the external arrival rate to service center i. <var>lambda</var>
+must be a vector of length N, <var>lambda</var><code>(i) &ge; 0</code>.
+
+          <br><dt><var>S</var><dd><var>S</var><code>(i)</code> is the average service time on service center i
+<var>S</var> must be a vector of length N, <var>S</var><code>(i)&gt;0</code>.
+
+          <br><dt><var>P</var><dd><var>P</var><code>(i,j)</code> is the probability
+that a job which completes service at service center i proceeds
+to service center j. <var>P</var> must be a matrix of size
+N \times N.
+
+          <br><dt><var>m</var><dd><var>m</var><code>(i)</code> is the number of servers at service center
+i. If <var>m</var><code>(i) &lt; 1</code>, service center i is an
+infinite-server node. Otherwise, it is a regular FCFS queueing center with
+<var>m</var><code>(i)</code> servers. If this parameter is omitted, default is
+<var>m</var><code>(i) = 1</code> for all i. If this parameter is a scalar,
+it will be promoted to a vector with the same size as <var>lambda</var>. 
+Otherwise, <var>m</var> must be a vector of length N.
+
+          <br><dt><var>k</var><dd>Compute the steady-state probability that there are <var>k</var><code>(i)</code>
+requests at service center i. <var>k</var> must have the same length
+as <var>lambda</var>, with <var>k</var><code>(i) &ge; 0</code>.
+
+        </dl>
+
+        <p><strong>OUTPUT</strong>
+
+          <dl>
+<dt><var>U</var><dd>If i is a FCFS node, then
+<var>U</var><code>(i)</code> is the utilization of service center i. 
+If i is an IS node, then <var>U</var><code>(i)</code> is the
+<em>traffic intensity</em> defined as <var>X</var><code>(i)*</code><var>S</var><code>(i)</code>.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(i)</code> is the average response time of service center i.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(i)</code> is the average number of customers in service center
+i.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(i)</code> is the throughput of service center i.
+
+          <br><dt><var>pr</var><dd><var>pr</var><code>(i)</code> is the steady state probability
+that there are <var>k</var><code>(i)</code> requests at service center i.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnopen.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>This implementation is based on G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, <cite>Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications</cite>, Wiley,
+1998, pp. 284&ndash;287.
+
+   <p><a name="index-Bolch_002c-G_002e-92"></a><a name="index-Greiner_002c-S_002e-93"></a><a name="index-de-Meer_002c-H_002e-94"></a><a name="index-Trivedi_002c-K_002e-95"></a>
+
+<h4 class="subsection">6.3.2 The Convolution Algorithm</h4>
+
+<p>According to the BCMP theorem, the state probability of a closed
+single class queueing network with K nodes and N requests
+can be expressed as:
+
+<pre class="example">     k = [k1, k2, ... kn]; <span class="roman">population vector</span>
+     p = 1/G(N+1) \prod F(i,k);
+</pre>
+   <p>Here \pi(k_1, k_2, \ldots k_K) is the joint probability of
+having k_i requests at node i, for all i=1,2,
+\ldots K.
+
+   <p>The <em>convolution algorithms</em> computes the normalization constants
+G = (G(0), G(1), \ldots G(N)) for single-class, closed networks
+with N requests.  The normalization constants are returned as
+vector <var>G</var><code>=[</code><var>G</var><code>(1), </code><var>G</var><code>(2), ... </code><var>G</var><code>(N+1)]</code> where
+<var>G</var><code>(i+1)</code> is the value of G(i) (remember that Octave
+uses 1-base vectors). The normalization constant can be used to
+compute all performance measures of interest (utilization, average
+response time and so on).
+
+   <p><code>queueing</code> implements the convolution algorithm, in the function
+<code>qnconvolution</code> and <code>qnconvolutionld</code>. The first one
+supports single-station nodes, multiple-station nodes and IS nodes. 
+The second one supports networks with general load-dependent service
+centers.
+
+<!-- The Convolution Algorithm -->
+   <p><a name="doc_002dqnconvolution"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qnconvolution</b> (<var>N, S, V</var>)<var><a name="index-qnconvolution-96"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qnconvolution</b> (<var>N, S, V, m</var>)<var><a name="index-qnconvolution-97"></a></var><br>
+<blockquote>
+        <p><a name="index-closed-network-98"></a><a name="index-normalization-constant-99"></a><a name="index-convolution-algorithm-100"></a>
+This function implements the <em>convolution algorithm</em> for
+computing steady-state performance measures of product-form,
+single-class closed queueing networks. Load-independent service
+centers, multiple servers (M/M/m queues) and IS nodes are
+supported. For general load-dependent service centers, use the
+<code>qnconvolutionld</code> function instead.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Number of requests in the system (<var>N</var><code>&gt;0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the average service time on center k
+(<var>S</var><code>(k) &ge; 0</code>).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the visit count of service center k
+(<var>V</var><code>(k) &ge; 0</code>).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center
+k. If <var>m</var><code>(k) &lt; 1</code>, center k is a delay center (IS);
+if <var>m</var><code>(k) &ge; 1</code>, center k
+it is a regular M/M/m queueing center with <var>m</var><code>(k)</code>
+identical servers. Default is <var>m</var><code>(k) = 1</code> for all k.
+
+        </dl>
+
+        <p><strong>OUTPUT</strong>
+
+          <dl>
+<dt><var>U</var><dd><var>U</var><code>(k)</code> is the utilization of center k. 
+For IS nodes, <var>U</var><code>(k)</code> is the <em>traffic intensity</em>.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the average response time of center k.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(k)</code> is the average number of customers at center
+k.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(k)</code> is the throughput of center k.
+
+          <br><dt><var>G</var><dd>Vector of normalization constants. <var>G</var><code>(n+1)</code> contains the value of
+the normalization constant with n requests
+G(n), n=0, <small class="dots">...</small>, N.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnconvolutionld.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+   <p>The normalization constant G can be used to compute the
+steady-state probabilities for a closed single class product-form
+Queueing Network with K nodes. Let <var>k</var><code>=[k_1,
+k_2, ... k_K]</code> be a valid population vector. Then, the
+steady-state probability <var>p</var><code>(i)</code> to have <var>k</var><code>(i)</code>
+requests at service center i can be computed as:
+
+<pre class="example"><pre class="verbatim">      k = [1 2 0];
+      K = sum(k); # Total population size
+      S = [ 1/0.8 1/0.6 1/0.4 ];
+      m = [ 2 3 1 ];
+      V = [ 1 .667 .2 ];
+      [U R Q X G] = qnconvolution( K, S, V, m );
+      p = [0 0 0]; # initialize p
+      # Compute the probability to have k(i) jobs at service center i
+      for i=1:3
+        p(i) = (V(i)*S(i))^k(i) / G(K+1) * \
+               (G(K-k(i)+1) - V(i)*S(i)*G(K-k(i)) );
+        printf("k(%d)=%d prob=%f\n", i, k(i), p(i) );
+      endfor</pre>-| k(1)=1 prob=0.17975
+     -| k(2)=2 prob=0.48404
+     -| k(3)=0 prob=0.52779
+</pre>
+   <p class="noindent"><strong>NOTE</strong>
+
+   <p>For a network with K service centers and N requests,
+this implementation of the convolution algorithm has time and space
+complexity O(NK).
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Jeffrey P. Buzen, <cite>Computational Algorithms for Closed Queueing
+Networks with Exponential Servers</cite>, Communications of the ACM, volume
+16, number 9, september 1973,
+pp. 527&ndash;531. <a href="http://doi.acm.org/10.1145/362342.362345">http://doi.acm.org/10.1145/362342.362345</a>
+
+   <p><a name="index-Buzen_002c-J_002e-P_002e-101"></a>
+This implementation is based on G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, <cite>Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications</cite>, Wiley,
+1998, pp. 313&ndash;317.
+
+   <p><a name="index-Bolch_002c-G_002e-102"></a><a name="index-Greiner_002c-S_002e-103"></a><a name="index-de-Meer_002c-H_002e-104"></a><a name="index-Trivedi_002c-K_002e-105"></a>
+<!-- Convolution for load-dependent service centers -->
+<a name="doc_002dqnconvolutionld"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qnconvolutionld</b> (<var>N, S, V</var>)<var><a name="index-qnconvolutionld-106"></a></var><br>
+<blockquote>
+        <p><a name="index-closed-network-107"></a><a name="index-normalization-constant-108"></a><a name="index-convolution-algorithm-109"></a><a name="index-load_002ddependent-service-center-110"></a>
+This function implements the <em>convolution algorithm</em> for
+product-form, single-class closed queueing networks with general
+load-dependent service centers.
+
+        <p>This function computes steady-state performance measures for
+single-class, closed networks with load-dependent service centers
+using the convolution algorithm; the normalization constants are also
+computed. The normalization constants are returned as vector
+<var>G</var><code>=[</code><var>G</var><code>(1), ..., </code><var>G</var><code>(N+1)]</code> where
+<var>G</var><code>(i+1)</code> is the value of G(i).
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Number of requests in the system (<var>N</var><code>&gt;0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k,n)</code> is the mean service time at center k
+where there are n requests, 1 &le; n
+&le; N. <var>S</var><code>(k,n)</code> = 1 / \mu_k,n,
+where \mu_k,n is the service rate of center k
+when there are n requests.
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the visit count of service center k
+(<var>V</var><code>(k) &ge; 0</code>). The length of <var>V</var> is the number of
+servers K in the network.
+
+        </dl>
+
+        <p><strong>OUTPUT</strong>
+
+          <dl>
+<dt><var>U</var><dd><var>U</var><code>(k)</code> is the utilization of center k.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the average response time at center k.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(k)</code> is the average number of customers in center k.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(k)</code> is the throughput of center k.
+
+          <br><dt><var>G</var><dd>Normalization constants (vector). <var>G</var><code>(n+1)</code>
+corresponds to G(n), as array indexes in Octave start
+from 1.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnconvolution.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Herb Schwetman, <cite>Some Computational Aspects of Queueing Network
+Models</cite>, Technical Report CSD-TR-354, Department of Computer Sciences,
+Purdue University, feb, 1981 (revised). 
+<a href="http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-354.pdf">http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-354.pdf</a>
+
+   <p><a name="index-Schwetman_002c-H_002e-111"></a>
+M. Reiser, H. Kobayashi, <cite>On The Convolution Algorithm for
+Separable Queueing Networks</cite>, In Proceedings of the 1976 ACM
+SIGMETRICS Conference on Computer Performance Modeling Measurement and
+Evaluation (Cambridge, Massachusetts, United States, March 29&ndash;31,
+1976). SIGMETRICS '76. ACM, New York, NY,
+pp. 109&ndash;117. <a href="http://doi.acm.org/10.1145/800200.806187">http://doi.acm.org/10.1145/800200.806187</a>
+
+   <p><a name="index-Reiser_002c-M_002e-112"></a><a name="index-Kobayashi_002c-H_002e-113"></a>
+This implementation is based on G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, <cite>Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications</cite>, Wiley,
+1998, pp. 313&ndash;317. Function <code>qnconvolutionld</code> is slightly
+different from the version described in Bolch et al. because it
+supports general load-dependent centers (while the version in the book
+does not). The modification is in the definition of function
+<code>F()</code> in <code>qnconvolutionld</code> which has been made similar to
+function f_i defined in Schwetman, <code>Some Computational
+Aspects of Queueing Network Models</code>.
+
+   <p><a name="index-Bolch_002c-G_002e-114"></a><a name="index-Greiner_002c-S_002e-115"></a><a name="index-de-Meer_002c-H_002e-116"></a><a name="index-Trivedi_002c-K_002e-117"></a>
+
+<h4 class="subsection">6.3.3 Open networks</h4>
+
+<!-- Open networks with single class -->
+<p><a name="doc_002dqnopensingle"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnopensingle</b> (<var>lambda, S, V</var>)<var><a name="index-qnopensingle-118"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnopensingle</b> (<var>lambda, S, V, m</var>)<var><a name="index-qnopensingle-119"></a></var><br>
+<blockquote>
+        <p><a name="index-open-network_002c-single-class-120"></a><a name="index-BCMP-network-121"></a>
+Analyze open, single class BCMP queueing networks.
+
+        <p>This function works for a subset of BCMP single-class open networks
+satisfying the following properties:
+
+          <ul>
+<li>The allowed service disciplines at network nodes are: FCFS,
+PS, LCFS-PR, IS (infinite server);
+
+          <li>Service times are exponentially distributed and
+load-independent;
+
+          <li>Service center i can consist of <var>m</var><code>(i) &ge; 1</code>
+identical servers.
+
+          <li>Routing is load-independent
+
+        </ul>
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Overall external arrival rate (<var>lambda</var><code>&gt;0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the average service time at center
+i (<var>S</var><code>(k)&gt;0</code>).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the average number of visits to center
+k (<var>V</var><code>(k) &ge; 0</code>).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center i. If
+<var>m</var><code>(k) &lt; 1</code>, then service center k is a delay center
+(IS); otherwise it is a regular queueing center with
+<var>m</var><code>(k)</code> servers. Default is <var>m</var><code>(k) = 1</code> for each
+k.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>If k is a queueing center,
+<var>U</var><code>(k)</code> is the utilization of center k. 
+If k is an IS node, then <var>U</var><code>(k)</code> is the
+<em>traffic intensity</em> defined as <var>X</var><code>(k)*</code><var>S</var><code>(k)</code>.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the average response time of center k.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(k)</code> is the average number of requests at center
+k.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(k)</code> is the throughput of center k.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnopen,qnclosed,qnvisits.
+
+        </blockquote></div>
+
+   <p>From the results computed by this function, it is possible to derive
+other quantities of interest as follows:
+
+     <ul>
+<li><strong>System Response Time</strong>: The overall system response time
+can be computed as
+<code>R_s = dot(V,R);</code>
+
+     <li><strong>Average number of requests</strong>: The average number of requests
+in the system can be computed as:
+<code>Q_s = sum(Q)</code>
+
+   </ul>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+<pre class="example"><pre class="verbatim">      lambda = 3;
+      V = [16 7 8];
+      S = [0.01 0.02 0.03];
+      [U R Q X] = qnopensingle( lambda, S, V );
+      R_s = dot(R,V) # System response time
+      N = sum(Q) # Average number in system</pre>-| R_s =  1.4062
+     -| N =  4.2186
+</pre>
+   <p class="noindent"><strong>REFERENCES</strong>
+
+   <p>G. Bolch, S. Greiner, H. de Meer and K. Trivedi, <cite>Queueing
+Networks and Markov Chains: Modeling and Performance Evaluation with
+Computer Science Applications</cite>, Wiley, 1998.
+
+   <p><a name="index-Bolch_002c-G_002e-122"></a><a name="index-Greiner_002c-S_002e-123"></a><a name="index-de-Meer_002c-H_002e-124"></a><a name="index-Trivedi_002c-K_002e-125"></a>
+
+<!-- Open network with multiple classes -->
+   <p><a name="doc_002dqnopenmulti"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnopenmulti</b> (<var>lambda, S, V</var>)<var><a name="index-qnopenmulti-126"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnopenmulti</b> (<var>lambda, S, V, m</var>)<var><a name="index-qnopenmulti-127"></a></var><br>
+<blockquote>
+        <p><a name="index-open-network_002c-multiple-classes-128"></a>
+Exact analysis of open, multiple-class BCMP networks. The network can
+be made of <em>single-server</em> queueing centers (FCFS, LCFS-PR or
+PS) or delay centers (IS). This function assumes a network with
+K service centers and C customer classes.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd><var>lambda</var><code>(c)</code> is the external
+arrival rate of class c customers (<var>lambda</var><code>(c)&gt;0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(c,k)</code> is the mean service time of class c
+customers on the service center k (<var>S</var><code>(c,k)&gt;0</code>). 
+For FCFS nodes, average service times must be class-independent.
+
+          <br><dt><var>V</var><dd><var>V</var><code>(c,k)</code> is the average number of visits of class c
+customers to service center k (<var>V</var><code>(c,k) &ge; 0 </code>).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at service center
+k. Valid values are <var>m</var><code>(k) &lt; 1</code> to denote a delay
+center (-/G/\infty), and <var>m</var><code>(k)==1</code> to denote
+a single server queueing center (M/M/1&ndash;FCFS,
+-/G/1&ndash;LCFS-PR or -/G/1&ndash;PS).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>If k is a queueing center, then <var>U</var><code>(c,k)</code> is the
+class c utilization of center k. If k is
+an IS node, then <var>U</var><code>(c,k)</code> is the
+class c <em>traffic intensity</em>
+defined as <var>X</var><code>(c,k)*</code><var>S</var><code>(c,k)</code>.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(c,k)</code> is the class c response time at
+center k. The system response time for
+class c requests can be computed
+as <code>dot(</code><var>R</var><code>, </code><var>V</var><code>, 2)</code>.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(c,k)</code> is the average number of class c requests
+at center k. The average number of class c requests
+in the system <var>Qc</var> can be computed as <code>Qc = sum(</code><var>Q</var><code>, 2)</code>
+
+          <br><dt><var>X</var><dd><var>X</var><code>(c,k)</code> is the class c throughput
+at center k.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnopen,qnopensingle,qnvisits.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Edward D. Lazowska, John Zahorjan, G. Scott Graham, and Kenneth C. 
+Sevcik, <cite>Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models</cite>, Prentice Hall,
+1984. <a href="http://www.cs.washington.edu/homes/lazowska/qsp/">http://www.cs.washington.edu/homes/lazowska/qsp/</a>. In
+particular, see section 7.4.1 ("Open Model Solution Techniques").
+
+   <p><a name="index-Lazowska_002c-E_002e-D_002e-129"></a><a name="index-Zahorjan_002c-J_002e-130"></a><a name="index-Graham_002c-G_002e-S_002e-131"></a><a name="index-Sevcik_002c-K_002e-C_002e-132"></a>
+
+<h4 class="subsection">6.3.4 Closed Networks</h4>
+
+<!-- MVA for single class, closed networks -->
+<p><a name="doc_002dqnclosedsinglemva"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qnclosedsinglemva</b> (<var>N, S, V</var>)<var><a name="index-qnclosedsinglemva-133"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qnclosedsinglemva</b> (<var>N, S, V, m</var>)<var><a name="index-qnclosedsinglemva-134"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qnclosedsinglemva</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qnclosedsinglemva-135"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029-136"></a><a name="index-closed-network_002c-single-class-137"></a><a name="index-normalization-constant-138"></a>
+Analyze closed, single class queueing networks using the exact Mean
+Value Analysis (MVA) algorithm. The following queueing disciplines
+are supported: FCFS, LCFS-PR, PS and IS (Infinite Server). This
+function supports fixed-rate service centers or multiple server
+nodes. For general load-dependent service centers, use the function
+<code>qnclosedsinglemvald</code> instead.
+
+        <p>Additionally, the normalization constant G(n), n=0,
+<small class="dots">...</small>, N is computed; G(n) can be used in conjunction with
+the BCMP theorem to compute steady-state probabilities.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Population size (number of requests in the system, <var>N</var><code> &ge; 0</code>). 
+If <var>N</var><code> == 0</code>, this function returns
+<var>U</var><code> = </code><var>R</var><code> = </code><var>Q</var><code> = </code><var>X</var><code> = 0</code>
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the mean service time on server k
+(<var>S</var><code>(k)&gt;0</code>).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the average number of visits to service center
+k (<var>V</var><code>(k) &ge; 0</code>).
+
+          <br><dt><var>Z</var><dd>External delay for customers (<var>Z</var><code> &ge; 0</code>). Default is 0.
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center k
+(if <var>m</var> is a scalar, all centers have that number of servers). If
+<var>m</var><code>(k) &lt; 1</code>, center k is a delay center (IS);
+otherwise it is a regular queueing center (FCFS, LCFS-PR or PS) with
+<var>m</var><code>(k)</code> servers. Default is <var>m</var><code>(k) = 1</code> for all
+k (each service center has a single server).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>If k is a FCFS, LCFS-PR or PS node (<var>m</var><code>(k) == 1</code>),
+then <var>U</var><code>(k)</code> is the utilization of center k. If
+k is an IS node (<var>m</var><code>(k) &lt; 1</code>), then
+<var>U</var><code>(k)</code> is the <em>traffic intensity</em> defined as
+<var>X</var><code>(k)*</code><var>S</var><code>(k)</code>.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the response time at center k. 
+The system response time <var>Rsys</var>
+can be computed as <var>Rsys</var><code> = </code><var>N</var><code>/</code><var>Xsys</var><code> - Z</code>
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(k)</code> is the average number of requests at center
+k. The number of requests in the system can be computed
+either as <code>sum(</code><var>Q</var><code>)</code>, or using the formula
+<var>N</var><code>-</code><var>Xsys</var><code>*</code><var>Z</var>.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(k)</code> is the throughput of center k. The
+system throughput <var>Xsys</var> can be computed as
+<var>Xsys</var><code> = </code><var>X</var><code>(1) / </code><var>V</var><code>(1)</code>
+
+          <br><dt><var>G</var><dd>Normalization constants. <var>G</var><code>(n+1)</code> corresponds to the value
+of the normalization constant G(n), n=0, <small class="dots">...</small>, N as
+array indexes in Octave start from 1. G(n) can be used in
+conjunction with the BCMP theorem to compute steady-state
+probabilities.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedsinglemvald.
+
+        </blockquote></div>
+
+   <p>From the results provided by this function, it is possible to derive
+other quantities of interest as follows:
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+<pre class="example"><pre class="verbatim">      S = [ 0.125 0.3 0.2 ];
+      V = [ 16 10 5 ];
+      N = 20;
+      m = ones(1,3);
+      Z = 4;
+      [U R Q X] = qnclosedsinglemva(N,S,V,m,Z);
+      X_s = X(1)/V(1); # System throughput
+      R_s = dot(R,V); # System response time
+      printf("\t    Util      Qlen     RespT      Tput\n");
+      printf("\t--------  --------  --------  --------\n");
+      for k=1:length(S)
+        printf("Dev%d\t%8.4f  %8.4f  %8.4f  %8.4f\n", k, U(k), Q(k), R(k), X(k) );
+      endfor
+      printf("\nSystem\t          %8.4f  %8.4f  %8.4f\n\n", N-X_s*Z, R_s, X_s );</pre></pre>
+   <p class="noindent"><strong>REFERENCES</strong>
+
+   <p>M. Reiser and S. S. Lavenberg, <cite>Mean-Value Analysis of Closed
+Multichain Queuing Networks</cite>, Journal of the ACM, vol. 27, n. 2, April
+1980, pp. 313&ndash;322. <a href="http://doi.acm.org/10.1145/322186.322195">http://doi.acm.org/10.1145/322186.322195</a>
+
+   <p><a name="index-Reiser_002c-M_002e-139"></a><a name="index-Lavenberg_002c-S_002e-S_002e-140"></a>
+This implementation is described in R. Jain , <cite>The Art of Computer
+Systems Performance Analysis</cite>, Wiley, 1991, p. 577.  Multi-server nodes
+<!-- and the computation of @math{G(N)}, -->
+are treated according to G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, <cite>Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications</cite>, Wiley,
+1998, Section 8.2.1, "Single Class Queueing Networks".
+
+   <p><a name="index-Jain_002c-R_002e-141"></a><a name="index-Bolch_002c-G_002e-142"></a><a name="index-Greiner_002c-S_002e-143"></a><a name="index-de-Meer_002c-H_002e-144"></a><a name="index-Trivedi_002c-K_002e-145"></a>
+<!-- MVA for single class, closed networks with load dependent servers -->
+<a name="doc_002dqnclosedsinglemvald"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedsinglemvald</b> (<var>N, S, V</var>)<var><a name="index-qnclosedsinglemvald-146"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedsinglemvald</b> (<var>N, S, V, Z</var>)<var><a name="index-qnclosedsinglemvald-147"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029-148"></a><a name="index-closed-network_002c-single-class-149"></a><a name="index-load_002ddependent-service-center-150"></a>
+Exact MVA algorithm for closed, single class queueing networks
+with load-dependent service centers. This function supports
+FCFS, LCFS-PR, PS and IS nodes. For networks with only fixed-rate
+service centers and multiple-server nodes, the function
+<code>qnclosedsinglemva</code> is more efficient.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Population size (number of requests in the system, <var>N</var><code> &ge; 0</code>). 
+If <var>N</var><code> == 0</code>, this function returns <var>U</var><code> = </code><var>R</var><code> = </code><var>Q</var><code> = </code><var>X</var><code> = 0</code>
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k,n)</code> is the mean service time at center k
+where there are n requests, 1 &le; n
+&le; N. <var>S</var><code>(k,n)</code> = 1 / \mu_k,n,
+where \mu_k,n is the service rate of center k
+when there are n requests.
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the average number
+of visits to service center k (<var>V</var><code>(k) &ge; 0</code>).
+
+          <br><dt><var>Z</var><dd>external delay ("think time", <var>Z</var><code> &ge; 0</code>); default 0.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd><var>U</var><code>(k)</code> is the utilization of service center k. The
+utilization is defined as the probability that service center
+k is not empty, that is, U_k = 1-\pi_k(0) where
+\pi_k(0) is the steady-state probability that there are 0
+jobs at service center k.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the response time on service center k.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(k)</code> is the average number of requests in service center
+k.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(k)</code> is the throughput of service center k.
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>M. Reiser and S. S. Lavenberg, <cite>Mean-Value Analysis of Closed
+Multichain Queuing Networks</cite>, Journal of the ACM, vol. 27, n. 2,
+April 1980, pp. 313&ndash;322. <a href="http://doi.acm.org/10.1145/322186.322195">http://doi.acm.org/10.1145/322186.322195</a>
+
+   <p>This implementation is described in G. Bolch, S. Greiner, H. de Meer
+and K. Trivedi, <cite>Queueing Networks and Markov Chains: Modeling
+and Performance Evaluation with Computer Science Applications</cite>, Wiley,
+1998, Section 8.2.4.1, &ldquo;Networks with Load-Deèpendent Service: Closed
+Networks&rdquo;.
+
+   <p><a name="index-Bolch_002c-G_002e-151"></a><a name="index-Greiner_002c-S_002e-152"></a><a name="index-de-Meer_002c-H_002e-153"></a><a name="index-Trivedi_002c-K_002e-154"></a>
+<!-- CMVA for single class, closed networks with a single load dependent servers -->
+<a name="doc_002dqncmva"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmva</b> (<var>N, S, Sld, V</var>)<var><a name="index-qncmva-155"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmva</b> (<var>N, S, Sld, V, Z</var>)<var><a name="index-qncmva-156"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029-157"></a><a name="index-CMVA-158"></a>
+Implementation of the Conditional MVA (CMVA) algorithm, a numerically
+stable variant of MVA for load-dependent servers. CMVA is described
+in G. Casale, <cite>A Note on Stable Flow-Equivalent Aggregation in
+Closed Networks</cite>. The network is made of M service centers and
+a delay center. Servers 1, \ldots, M-1 are load-independent;
+server M is load-dependent.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Population size (number of requests in the system, <var>N</var><code> &ge; 0</code>). 
+If <var>N</var><code> == 0</code>, this function returns
+<var>U</var><code> = </code><var>R</var><code> = </code><var>Q</var><code> = </code><var>X</var><code> = 0</code>
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the mean service time on server k = 1, <small class="dots">...</small>, M-1
+(<var>S</var><code>(k) &gt; 0</code>).
+
+          <br><dt><var>Sld</var><dd><var>Sld</var><code>(n)</code> is the mean service time on server M
+when there are n requests, n=1, <small class="dots">...</small>, N. 
+<var>Sld</var><code>(n) = </code> 1 / \mu(n), where \mu(n) is the
+service rate at center N when there are n requests.
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the average number of visits to service center
+k= 1, <small class="dots">...</small>, M (<var>V</var><code>(k) &ge; 0</code>).
+
+          <br><dt><var>Z</var><dd>External delay for customers (<var>Z</var><code> &ge; 0</code>). Default is 0.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd><var>U</var><code>(k)</code> is the utilization of center k=1, <small class="dots">...</small>, M
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the response time at center k=1, <small class="dots">...</small>, M. 
+The system response time <var>Rsys</var>
+can be computed as <var>Rsys</var><code> = </code><var>N</var><code>/</code><var>Xsys</var><code> - Z</code>
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(k)</code> is the average number of requests at center
+k=1, <small class="dots">...</small>, M.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(k)</code> is the throughput of center k=1, <small class="dots">...</small>, M.
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>G. Casale. <cite>A note on stable flow-equivalent aggregation in
+closed networks</cite>. Queueing Syst. Theory Appl., 60:193–202, December
+2008.
+
+   <p><a name="index-Casale_002c-G_002e-159"></a>
+<!-- Approximate MVA for single class, closed networks -->
+
+   <p><a name="doc_002dqnclosedsinglemvaapprox"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedsinglemvaapprox</b> (<var>N, S, V</var>)<var><a name="index-qnclosedsinglemvaapprox-160"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedsinglemvaapprox</b> (<var>N, S, V, m</var>)<var><a name="index-qnclosedsinglemvaapprox-161"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedsinglemvaapprox</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qnclosedsinglemvaapprox-162"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedsinglemvaapprox</b> (<var>N, S, V, m, Z, tol</var>)<var><a name="index-qnclosedsinglemvaapprox-163"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedsinglemvaapprox</b> (<var>N, S, V, m, Z, tol, iter_max</var>)<var><a name="index-qnclosedsinglemvaapprox-164"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029_002c-approximate-165"></a><a name="index-Approximate-MVA-166"></a><a name="index-Closed-network_002c-single-class-167"></a><a name="index-Closed-network_002c-approximate-analysis-168"></a>
+Analyze closed, single class queueing networks using the Approximate
+Mean Value Analysis (MVA) algorithm. This function is based on
+approximating the number of customers seen at center k when a
+new request arrives as Q_k(N) \times (N-1)/N. This function
+only handles single-server and delay centers; if your network
+contains general load-dependent service centers, use the function
+<code>qnclosedsinglemvald</code> instead.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Population size (number of requests in the system, <var>N</var><code> &gt; 0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the mean service time on server k
+(<var>S</var><code>(k)&gt;0</code>).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the average number of visits to service center
+k (<var>V</var><code>(k) &ge; 0</code>).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center k
+(if <var>m</var> is a scalar, all centers have that number of servers). If
+<var>m</var><code>(k) &lt; 1</code>, center k is a delay center (IS); if
+<var>m</var><code>(k) == 1</code>, center k is a regular queueing
+center (FCFS, LCFS-PR or PS) with one server (default). This function
+does not support multiple server nodes (<var>m</var><code>(k) &gt; 1</code>).
+
+          <br><dt><var>Z</var><dd>External delay for customers (<var>Z</var><code> &ge; 0</code>). Default is 0.
+
+          <br><dt><var>tol</var><dd>Stopping tolerance. The algorithm stops when the maximum relative difference
+between the new and old value of the queue lengths <var>Q</var> becomes
+less than the tolerance. Default is 10^-5.
+
+          <br><dt><var>iter_max</var><dd>Maximum number of iterations (<var>iter_max</var><code>&gt;0</code>. 
+The function aborts if convergenge is not reached within the maximum
+number of iterations. Default is 100.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>If k is a FCFS, LCFS-PR or PS node (<var>m</var><code>(k) == 1</code>),
+then <var>U</var><code>(k)</code> is the utilization of center k. If
+k is an IS node (<var>m</var><code>(k) &lt; 1</code>), then
+<var>U</var><code>(k)</code> is the <em>traffic intensity</em> defined as
+<var>X</var><code>(k)*</code><var>S</var><code>(k)</code>.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the response time at center k. 
+The system response time <var>Rsys</var>
+can be computed as <var>Rsys</var><code> = </code><var>N</var><code>/</code><var>Xsys</var><code> - Z</code>
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(k)</code> is the average number of requests at center
+k. The number of requests in the system can be computed
+either as <code>sum(</code><var>Q</var><code>)</code>, or using the formula
+<var>N</var><code>-</code><var>Xsys</var><code>*</code><var>Z</var>.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(k)</code> is the throughput of center k. The
+system throughput <var>Xsys</var> can be computed as
+<var>Xsys</var><code> = </code><var>X</var><code>(1) / </code><var>V</var><code>(1)</code>
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedsinglemva,qnclosedsinglemvald.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>This implementation is based on Edward D. Lazowska, John Zahorjan,
+G. Scott Graham, and Kenneth C. Sevcik, <cite>Quantitative System
+Performance: Computer System Analysis Using Queueing Network Models</cite>,
+Prentice Hall,
+1984. <a href="http://www.cs.washington.edu/homes/lazowska/qsp/">http://www.cs.washington.edu/homes/lazowska/qsp/</a>. In
+particular, see section 6.4.2.2 ("Approximate Solution Techniques").
+
+   <p><a name="index-Lazowska_002c-E_002e-D_002e-169"></a><a name="index-Zahorjan_002c-J_002e-170"></a><a name="index-Graham_002c-G_002e-S_002e-171"></a><a name="index-Sevcik_002c-K_002e-C_002e-172"></a>
+
+<!-- MVA for multiple class, closed networks -->
+   <p><a name="doc_002dqnclosedmultimva"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimva</b> (<var>N, S </var>)<var><a name="index-qnclosedmultimva-173"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimva</b> (<var>N, S, V</var>)<var><a name="index-qnclosedmultimva-174"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimva</b> (<var>N, S, V, m</var>)<var><a name="index-qnclosedmultimva-175"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimva</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qnclosedmultimva-176"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimva</b> (<var>N, S, P</var>)<var><a name="index-qnclosedmultimva-177"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimva</b> (<var>N, S, P, m</var>)<var><a name="index-qnclosedmultimva-178"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029-179"></a><a name="index-closed-network_002c-multiple-classes-180"></a>
+Analyze closed, multiclass queueing networks with K service
+centers and C independent customer classes (chains) using the
+Mean Value Analysys (MVA) algorithm.
+
+        <p>Queueing policies at service centers can be any of the following:
+
+          <dl>
+<dt><strong>FCFS</strong><dd>(First-Come-First-Served) customers are served in order of arrival;
+multiple servers are allowed. For this kind of queueing discipline,
+average service times must be class-independent.
+
+          <br><dt><strong>PS</strong><dd>(Processor Sharing) customers are served in parallel by a single
+server, each customer receiving an equal share of the service rate.
+
+          <br><dt><strong>LCFS-PR</strong><dd>(Last-Come-First-Served, Preemptive Resume) customers are served in
+reverse order of arrival by a single server and the last arrival
+preempts the customer in service who will later resume service at the
+point of interruption.
+
+          <br><dt><strong>IS</strong><dd>(Infinite Server) customers are delayed independently of other
+customers at the service center (there is effectively an infinite
+number of servers).
+
+        </dl>
+
+        <blockquote>
+<b>Note:</b> If this function is called specifying the visit ratios
+<var>V</var>, class switching is <strong>not</strong> allowed.
+
+        <p>If this function is called specifying the routing probability matrix
+<var>P</var>, then class switching <strong>is</strong> allowed; however, in this
+case all nodes are restricted to be fixed rate service centers or
+delay centers: multiple-server and general load-dependent
+centers are not supported.</blockquote>
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd><var>N</var><code>(c)</code> is the number of class c requests in the
+system; <var>N</var><code>(c) &ge; 0</code>. If class c has
+no requests (<var>N</var><code>(c) = 0</code>), then
+<var>U</var><code>(c,k) = </code><var>R</var><code>(c,k) = </code><var>Q</var><code>(c,k) = </code><var>X</var><code>(c,k) = 0</code>
+for all <var>k</var>.
+
+          <br><dt><var>S</var><dd><var>S</var><code>(c,k)</code> is the mean service time for class c
+customers at center k (<var>S</var><code>(c,k) &ge; 0</code>). 
+If service time at center k is class-dependent,
+then center #mathk is assumed to be of type -/G/1&ndash;PS
+(Processor Sharing). 
+If center k is a FCFS node (<var>m</var><code>(k)&gt;1</code>), then the
+service times <strong>must</strong> be class-independent.
+
+          <br><dt><var>V</var><dd><var>V</var><code>(c,k)</code> is the average number of visits of class c
+customers to service center k; <var>V</var><code>(c,k) &ge; 0</code>,
+default is 1. 
+<strong>If you pass this parameter, no class switching is not
+allowed</strong>
+
+          <br><dt><var>P</var><dd><var>P</var><code>(r,i,s,j)</code> is the probability that a class r
+job completing service at center i is routed to center j
+as a class s job. <strong>If you pass this parameter,
+class switching is allowed</strong>.
+
+          <br><dt><var>m</var><dd>If <var>m</var><code>(k)&lt;1</code>, then center k is assumed to be a delay
+center (IS node -/G/\infty). If <var>m</var><code>(k)==1</code>, then
+service center k is a regular queueing center
+(M/M/1&ndash;FCFS, -/G/1&ndash;LCFS-PR or -/G/1&ndash;PS). 
+Finally, if <var>m</var><code>(k)&gt;1</code>, center k is a
+M/M/m&ndash;FCFS center with <var>m</var><code>(k)</code> identical servers. 
+Default is <var>m</var><code>(k)=1</code> for each k.
+
+          <br><dt><var>Z</var><dd><var>Z</var><code>(c)</code> is the class c external delay (think time);
+<var>Z</var><code>(c) &ge; 0</code>. Default is 0.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>If k is a FCFS, LCFS-PR or PS node, then <var>U</var><code>(c,k)</code>
+is the class c utilization at center
+k. If k is an IS node, then <var>U</var><code>(c,k)</code> is the
+class c <em>traffic intensity</em> at center k,
+defined as <var>U</var><code>(c,k) = </code><var>X</var><code>(c,k)*</code><var>S</var><code>(c,k)</code>.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(c,k)</code> is the class c response time at
+center k. The total class c system response time
+can be computed as <code>dot(</code><var>R</var><code>, </code><var>V</var><code>, 2)</code>.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(c,k)</code> is the average number of
+class c requests at center k. The total number of
+requests at center k is <code>sum(</code><var>Q</var><code>(:,k))</code>. 
+The total number of class c requests in the system
+is <code>sum(</code><var>Q</var><code>(c,:))</code>.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(c,k)</code> is the class c throughput at
+center k. The class c system throughput can be computed
+as <var>X</var><code>(c,1) / </code><var>V</var><code>(c,1)</code>.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosed, qnclosedmultimvaapprox.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>NOTE</strong>
+
+   <p>Given a network with K service centers, C job classes and
+population vector \bf N=(N_1, N_2, \ldots N_C), the MVA
+algorithm requires space O(C \prod_i (N_i + 1)). The time
+complexity is O(CK\prod_i (N_i + 1)). This implementation is
+slightly more space-efficient (see details in the code). While the space
+requirement can be mitigated by using some optimizations, the time
+complexity can not. If you need to analyze large closed networks you
+should consider the <samp><span class="command">qnclosedmultimvaapprox</span></samp> function, which
+implements the approximate MVA algorithm. Note however that
+<samp><span class="command">qnclosedmultimvaapprox</span></samp> will only provide approximate results.
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>M. Reiser and S. S. Lavenberg, <cite>Mean-Value Analysis of Closed
+Multichain Queuing Networks</cite>, Journal of the ACM, vol. 27, n. 2, April
+1980, pp. 313&ndash;322. <a href="http://doi.acm.org/10.1145/322186.322195">http://doi.acm.org/10.1145/322186.322195</a>
+
+   <p><a name="index-Reiser_002c-M_002e-181"></a><a name="index-Lavenberg_002c-S_002e-S_002e-182"></a>
+This implementation is based on G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, <cite>Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications</cite>, Wiley,
+1998 and Edward D. Lazowska, John Zahorjan, G. Scott Graham, and
+Kenneth C. Sevcik, <cite>Quantitative System Performance: Computer
+System Analysis Using Queueing Network Models</cite>, Prentice Hall,
+1984. <a href="http://www.cs.washington.edu/homes/lazowska/qsp/">http://www.cs.washington.edu/homes/lazowska/qsp/</a>. In
+particular, see section 7.4.2.1 ("Exact Solution Techniques").
+
+   <p><a name="index-Bolch_002c-G_002e-183"></a><a name="index-Greiner_002c-S_002e-184"></a><a name="index-de-Meer_002c-H_002e-185"></a><a name="index-Trivedi_002c-K_002e-186"></a><a name="index-Lazowska_002c-E_002e-D_002e-187"></a><a name="index-Zahorjan_002c-J_002e-188"></a><a name="index-Graham_002c-G_002e-S_002e-189"></a><a name="index-Sevcik_002c-K_002e-C_002e-190"></a>
+<!-- Approximate MVA, with Bard-Schweitzer approximation -->
+<a name="doc_002dqnclosedmultimvaapprox"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimvaapprox</b> (<var>N, S, V</var>)<var><a name="index-qnclosedmultimvaapprox-191"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimvaapprox</b> (<var>N, S, V, m</var>)<var><a name="index-qnclosedmultimvaapprox-192"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimvaapprox</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qnclosedmultimvaapprox-193"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimvaapprox</b> (<var>N, S, V, m, Z, tol</var>)<var><a name="index-qnclosedmultimvaapprox-194"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosedmultimvaapprox</b> (<var>N, S, V, m, Z, tol, iter_max</var>)<var><a name="index-qnclosedmultimvaapprox-195"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029_002c-approximate-196"></a><a name="index-Approximate-MVA-197"></a><a name="index-Closed-network_002c-multiple-classes-198"></a><a name="index-Closed-network_002c-approximate-analysis-199"></a>
+Analyze closed, multiclass queueing networks with K service
+centers and C customer classes using the approximate Mean
+Value Analysys (MVA) algorithm.
+
+        <p>This implementation uses Bard and Schweitzer approximation. It is based
+on the assumption that
+the queue length at service center k with population
+set \bf N-\bf 1_c is approximately equal to the queue length
+with population set \bf N, times (n-1)/n:
+
+     <pre class="example">          Q_i(N-1c) ~ (n-1)/n Q_i(N)
+</pre>
+        <p>where \bf N is a valid population mix, \bf N-\bf 1_c
+is the population mix \bf N with one class c customer
+removed, and n = \sum_c N_c is the total number of requests.
+
+        <p>This implementation works for networks made of infinite server (IS)
+nodes and single-server nodes only.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd><var>N</var><code>(c)</code> is the number of
+class c requests in the system (<var>N</var><code>(c)&gt;0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(c,k)</code> is the mean service time for class c
+customers at center k (<var>S</var><code>(c,k) &ge; 0</code>).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(c,k)</code> is the average number of visits of class c
+requests to center k (<var>V</var><code>(c,k) &ge; 0</code>).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at service center
+k. If <var>m</var><code>(k) &lt; 1</code>, then the service center k
+is assumed to be a delay center (IS). If <var>m</var><code>(k) == 1</code>,
+service center k is a regular queueing center (FCFS, LCFS-PR
+or PS) with a single server node. If omitted, each service center has
+a single server. Note that multiple server nodes are not supported.
+
+          <br><dt><var>Z</var><dd><var>Z</var><code>(c)</code> is the class c external delay. Default
+is 0.
+
+          <br><dt><var>tol</var><dd>Stopping tolerance (<var>tol</var><code>&gt;0</code>). The algorithm stops if
+the queue length computed on two subsequent iterations are less than
+<var>tol</var>. Default is 10^-5.
+
+          <br><dt><var>iter_max</var><dd>Maximum number of iterations (<var>iter_max</var><code>&gt;0</code>. 
+The function aborts if convergenge is not reached within the maximum
+number of iterations. Default is 100.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>If k is a FCFS, LCFS-PR or PS node, then <var>U</var><code>(c,k)</code>
+is the utilization of class c requests on service center
+k. If k is an IS node, then <var>U</var><code>(c,k)</code> is the
+class c <em>traffic intensity</em> at device k,
+defined as <var>U</var><code>(c,k) = </code><var>X</var><code>(c)*</code><var>S</var><code>(c,k)</code>
+
+          <br><dt><var>R</var><dd><var>R</var><code>(c,k)</code> is the response
+time of class c requests at service center k.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(c,k)</code> is the average number of
+class c requests at service center k.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(c,k)</code> is the class c
+throughput at service center k.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosed.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Y. Bard, <cite>Some Extensions to Multiclass Queueing Network Analysis</cite>,
+proc. 4th Int. Symp. on Modelling and Performance Evaluation of
+Computer Systems, feb. 1979, pp. 51&ndash;62.
+
+   <p><a name="index-Bard_002c-Y_002e-200"></a>
+P. Schweitzer, <cite>Approximate Analysis of Multiclass Closed
+Networks of Queues</cite>, Proc. Int. Conf. on Stochastic Control and
+Optimization, jun 1979, pp. 25&ndash;29.
+
+   <p><a name="index-Schweitzer_002c-P_002e-201"></a>
+This implementation is based on Edward D. Lazowska, John Zahorjan, G. 
+Scott Graham, and Kenneth C. Sevcik, <cite>Quantitative System
+Performance: Computer System Analysis Using Queueing Network Models</cite>,
+Prentice Hall,
+1984. <a href="http://www.cs.washington.edu/homes/lazowska/qsp/">http://www.cs.washington.edu/homes/lazowska/qsp/</a>.  In
+particular, see section 7.4.2.2 ("Approximate Solution
+Techniques"). This implementation is slightly different from the one
+described above, as it computes the average response times R
+instead of the residence times.
+
+   <p><a name="index-Lazowska_002c-E_002e-D_002e-202"></a><a name="index-Zahorjan_002c-J_002e-203"></a><a name="index-Graham_002c-G_002e-S_002e-204"></a><a name="index-Sevcik_002c-K_002e-C_002e-205"></a>
+
+<h4 class="subsection">6.3.5 Mixed Networks</h4>
+
+<!-- MVA for mixed networks -->
+<p><a name="doc_002dqnmix"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnmix</b> (<var>lambda, N, S, V, m</var>)<var><a name="index-qnmix-206"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029-207"></a><a name="index-mixed-network-208"></a>
+Solution of mixed queueing networks through MVA. The network consists
+of K service centers (single-server or delay centers) and
+C independent customer chains. Both open and closed chains
+are possible. <var>lambda</var> is the vector of per-chain
+arrival rates (open classes); <var>N</var> is the vector of populations
+for closed chains.
+
+        <blockquote>
+<b>Note:</b> In this implementation class switching is <strong>not</strong> allowed. Each
+customer class <em>must</em> correspond to an independent chain. 
+</blockquote>
+
+        <p>If the network is made of open or closed classes only, then this
+function calls <code>qnopenmulti</code> or <code>qnclosedmultimva</code>
+respectively, and prints a warning message.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dt><var>N</var><dd>For each customer chain c:
+
+               <ul>
+<li>if c is a closed chain, then <var>N</var><code>(c)&gt;0</code> is the
+number of class c requests and <var>lambda</var><code>(c)</code> must be
+zero;
+
+               <li>If c is an open chain,
+<var>lambda</var><code>(c)&gt;0</code> is the arrival rate of class c
+requests and <var>N</var><code>(c)</code> must be zero;
+
+          </ul>
+
+          <p class="noindent">For each c, the following must hold:
+
+          <pre class="example">               (<var>lambda</var>(c)&gt;0 &amp;&amp; <var>N</var>(c)==0) || (<var>lambda</var>(c)==0 &amp;&amp; <var>N</var>(c)&gt;0)
+</pre>
+          <p>which means that either <var>lambda</var><code>(c)</code> is nonzero and
+<var>N</var><code>(n)</code> is zero, or the other way around. If for some
+c, <var>lambda</var>(c) \neq 0 and <var>N</var>(c) \neq 0, an
+error is reported and this function aborts.
+
+          <br><dt><var>S</var><dd><var>S</var><code>(c,k)</code> is the mean service time for class c
+customers on service center k, <var>S</var><code>(c,k) &ge; 0</code>. 
+For FCFS nodes, service times must be class-independent.
+
+          <br><dt><var>V</var><dd><var>V</var><code>(c,k)</code> is the average number of visits of class c
+customers to service center k (<var>V</var><code>(c,k) &ge; 0</code>).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at service center
+k. Only single-server (<var>m</var><code>(k)==1</code>) or IS (Infinite
+Server) nodes (<var>m</var><code>(k)&lt;1</code>) are supported. If omitted, each
+service center is assumed to have a single server. Queueing discipline
+for single-server nodes can be FCFS, PS or LCFS-PR.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd><var>U</var><code>(c,k)</code> is the
+utilization of class c requests on service center k.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(c,k)</code> is the response
+time of class c requests on service center k.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(c,k)</code> is the average number of
+class c requests on service center k.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(c,k)</code> is the class c
+throughput on service center k.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedmultimva, qnopenmulti.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Edward D. Lazowska, John Zahorjan, G. Scott Graham, and Kenneth C. 
+Sevcik, <cite>Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models</cite>, Prentice Hall,
+1984. <a href="http://www.cs.washington.edu/homes/lazowska/qsp/">http://www.cs.washington.edu/homes/lazowska/qsp/</a>. In
+particular, see section 7.4.3 ("Mixed Model Solution Techniques"). 
+Note that in this function we compute the mean response time R
+instead of the mean residence time as in the reference.
+
+   <p><a name="index-Lazowska_002c-E_002e-D_002e-209"></a><a name="index-Zahorjan_002c-J_002e-210"></a><a name="index-Graham_002c-G_002e-S_002e-211"></a><a name="index-Sevcik_002c-K_002e-C_002e-212"></a>
+Herb Schwetman, <cite>Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models</cite>, Technical Report CSD-TR-355,
+Department of Computer Sciences, Purdue University, feb 15, 1982,
+available at
+<a href="http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-355.pdf">http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-355.pdf</a>
+
+   <p><a name="index-Schwetman_002c-H_002e-213"></a>
+
+<div class="node">
+<a name="Algorithms-for-non-Product-form-QNs"></a>
+<a name="Algorithms-for-non-Product_002dform-QNs"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Bounds-on-performance">Bounds on performance</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">6.4 Algorithms for non Product-Form QNs</h3>
+
+<!-- MVABLO algorithm for approximate analysis of closed, single class -->
+<!-- QN with blocking -->
+<p><a name="doc_002dqnmvablo"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnmvablo</b> (<var>N, S, M, P</var>)<var><a name="index-qnmvablo-214"></a></var><br>
+<blockquote>
+        <p><a name="index-queueing-network-with-blocking-215"></a><a name="index-blocking-queueing-network-216"></a><a name="index-closed-network_002c-finite-capacity-217"></a>
+MVA algorithm for closed queueing networks with blocking. <samp><span class="command">qnmvablo</span></samp>
+computes approximate utilization, response time and mean queue length
+for closed, single class queueing networks with blocking.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>population size, i.e., number of requests in the system. <var>N</var> must
+be strictly greater than zero, and less than the overall network capacity:
+<code>0 &lt; </code><var>N</var><code> &lt; sum(</code><var>M</var><code>)</code>.
+
+          <br><dt><var>S</var><dd>Average service time. <var>S</var><code>(i)</code> is the average service time
+requested on server i (<var>S</var><code>(i) &gt; 0</code>).
+
+          <br><dt><var>M</var><dd>Server capacity. <var>M</var><code>(i)</code> is the capacity of service center
+i. The capacity is the maximum number of requests in a service
+center, including the request currently in service (<var>M</var><code>(i) &ge; 1</code>).
+
+          <br><dt><var>P</var><dd><var>P</var><code>(i,j)</code> is the probability that a request which completes
+service at server i will be transferred to server j.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd><var>U</var><code>(i)</code> is the utilization of
+service center i.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(i)</code> is the average response time
+of service center i.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(i)</code> is
+the average number of requests in service center i (including
+the request in service).
+
+          <br><dt><var>X</var><dd><var>X</var><code>(i)</code> is the throughput of
+service center i.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnopen, qnclosed.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Ian F. Akyildiz, <cite>Mean Value Analysis for Blocking Queueing
+Networks</cite>, IEEE Transactions on Software Engineering, vol. 14, n. 2,
+april 1988, pp. 418&ndash;428.  <a href="http://dx.doi.org/10.1109/32.4663">http://dx.doi.org/10.1109/32.4663</a>
+
+   <p><a name="index-Akyildiz_002c-I_002e-F_002e-218"></a>
+<a name="doc_002dqnmarkov"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnmarkov</b> (<var>lambda, S, C, P</var>)<var><a name="index-qnmarkov-219"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnmarkov</b> (<var>lambda, S, C, P, m</var>)<var><a name="index-qnmarkov-220"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnmarkov</b> (<var>N, S, C, P</var>)<var><a name="index-qnmarkov-221"></a></var><br>
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnmarkov</b> (<var>N, S, C, P, m</var>)<var><a name="index-qnmarkov-222"></a></var><br>
+<blockquote>
+        <p><a name="index-closed-network_002c-multiple-classes-223"></a><a name="index-closed-network_002c-finite-capacity-224"></a><a name="index-blocking-queueing-network-225"></a><a name="index-RS-blocking-226"></a>
+Compute utilization, response time, average queue length and
+throughput for open or closed queueing networks with finite capacity. 
+Blocking type is Repetitive-Service (RS). This function explicitly
+generates and solve the underlying Markov chain, and thus might
+require a large amount of memory.
+
+        <p>More specifically, networks which can me analyzed by this
+function have the following properties:
+
+          <ul>
+<li>There exists only a single class of customers.
+
+          <li>The network has K service centers. Center
+i has m_i &gt; 0 servers, and has a total (finite) capacity of
+C_i \geq m_i which includes both buffer space and servers. 
+The buffer space at service center i is therefore
+C_i - m_i.
+
+          <li>The network can be open, with external arrival rate to
+center i equal to
+\lambda_i, or closed with fixed
+population size N. For closed networks, the population size
+N must be strictly less than the network capacity: N &lt; \sum_i C_i.
+
+          <li>Average service times are load-independent.
+
+          <li>P_ij is the probability that requests completing
+execution at center i are transferred to
+center j, i \neq j. For open networks, a request may leave the system
+from any node i with probability 1-\sum_j P_ij.
+
+          <li>Blocking type is Repetitive-Service (RS). Service
+center j is <em>saturated</em> if the number of requests is equal
+to its capacity <code>C_j</code>. Under the RS blocking discipline,
+a request completing service at center i which is being
+transferred to a saturated server j is put back at the end of
+the queue of i and will receive service again. Center i
+then processes the next request in queue. External arrivals to a
+saturated servers are dropped.
+
+        </ul>
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dt><var>N</var><dd>If the first argument is a vector <var>lambda</var>, it is considered to be
+the external arrival rate <var>lambda</var><code>(i) &ge; 0</code> to service center
+i of an open network. If the first argument is a scalar, it is
+considered as the population size <var>N</var> of a closed network; in this case
+<var>N</var> must be strictly
+less than the network capacity: <var>N</var><code> &lt; sum(</code><var>C</var><code>)</code>.
+
+          <br><dt><var>S</var><dd><var>S</var><code>(i)</code> is the average service time at service center
+i
+
+          <br><dt><var>C</var><dd><var>C</var><code>(i)</code> is the Capacity of service center i. The capacity includes both
+the buffer and server space <var>m</var><code>(i)</code>. Thus the buffer space is
+<var>C</var><code>(i)-</code><var>m</var><code>(i)</code>.
+
+          <br><dt><var>P</var><dd><var>P</var><code>(i,j)</code> is the transition probability from service center
+i to service center j.
+
+          <br><dt><var>m</var><dd><var>m</var><code>(i)</code> is the number of servers at service center
+i. Note that <var>m</var><code>(i) &ge; </code><var>C</var><code>(i)</code> for each <var>i</var>. 
+If <var>m</var> is omitted, all service centers are assumed to have a
+single server (<var>m</var><code>(i) = 1</code> for all i).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd><var>U</var><code>(i)</code> is the utilization of service center i.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(i)</code> is the response time on service center i.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(i)</code> is the average number of customers in the
+service center i, <em>including</em> the request in service.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(i)</code> is the throughput of service center i.
+
+        </dl>
+
+        <blockquote>
+<b>Note:</b> 
+The space complexity of this implementation is
+O( \prod_i=1^K (C_i + 1)^2). The time complexity is dominated
+by the time needed to solve a linear system with
+\prod_i=1^K (C_i + 1)
+unknowns.
+
+        </blockquote>
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Bounds-on-performance"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Utility-functions">Utility functions</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">6.5 Bounds on performance</h3>
+
+<p><a name="doc_002dqnopenab"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>Xu</var>, <var>Rl</var>] = <b>qnopenab</b> (<var>lambda, D</var>)<var><a name="index-qnopenab-227"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-asymptotic-228"></a><a name="index-open-network-229"></a>
+Compute Asymptotic Bounds for single-class, open Queueing Networks
+with K service centers.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>overall arrival rate to the system (scalar). Abort if
+<var>lambda</var><code> &le; 0</code>
+
+          <br><dt><var>D</var><dd><var>D</var><code>(k)</code> is the service demand at center k. 
+The service demand vector <var>D</var> must be nonempty, and all demands
+must be nonnegative (<var>D</var><code>(k) &ge; 0</code> for all k).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xu</var><dd>Upper bound on the system throughput.
+
+          <br><dt><var>Rl</var><dd>Lower bound on the system response time.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnopenbsb.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Edward D. Lazowska, John Zahorjan, G.  Scott Graham, and Kenneth
+C. Sevcik, <cite>Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models</cite>, Prentice Hall,
+1984. <a href="http://www.cs.washington.edu/homes/lazowska/qsp/">http://www.cs.washington.edu/homes/lazowska/qsp/</a>. In
+particular, see section 5.2 ("Asymptotic Bounds").
+
+   <p><a name="index-Lazowska_002c-E_002e-D_002e-230"></a><a name="index-Zahorjan_002c-J_002e-231"></a><a name="index-Graham_002c-G_002e-S_002e-232"></a><a name="index-Sevcik_002c-K_002e-C_002e-233"></a>
+<a name="doc_002dqnclosedab"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnclosedab</b> (<var>N, D</var>)<var><a name="index-qnclosedab-234"></a></var><br>
+&mdash; Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnclosedab</b> (<var>N, D, Z</var>)<var><a name="index-qnclosedab-235"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-asymptotic-236"></a><a name="index-closed-network-237"></a>
+Compute Asymptotic Bounds for single-class, closed Queueing Networks
+with K service centers.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>number of requests in the system (scalar, <var>N</var><code>&gt;0</code>).
+
+          <br><dt><var>D</var><dd><var>D</var><code>(k)</code> is the service demand of service center k,
+<var>D</var><code>(k) &ge; 0</code>.
+
+          <br><dt><var>Z</var><dd>external delay (think time, scalar, <var>Z</var><code> &ge; 0</code>). If
+omitted, it is assumed to be zero.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dt><var>Xu</var><dd>Lower and upper bound on the system throughput.
+
+          <br><dt><var>Rl</var><dt><var>Ru</var><dd>Lower and upper bound on the system response time.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedbsb, qnclosedgb, qnclosedpb.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">Edward D. Lazowska, John Zahorjan, G.  Scott Graham, and Kenneth
+C. Sevcik, <cite>Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models</cite>, Prentice Hall,
+1984. <a href="http://www.cs.washington.edu/homes/lazowska/qsp/">http://www.cs.washington.edu/homes/lazowska/qsp/</a>. In
+particular, see section 5.2 ("Asymptotic Bounds").
+
+   <p><a name="index-Lazowska_002c-E_002e-D_002e-238"></a><a name="index-Zahorjan_002c-J_002e-239"></a><a name="index-Graham_002c-G_002e-S_002e-240"></a><a name="index-Sevcik_002c-K_002e-C_002e-241"></a>
+
+   <p><a name="doc_002dqnopenbsb"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnopenbsb</b> (<var>lambda, D</var>)<var><a name="index-qnopenbsb-242"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-balanced-system-243"></a><a name="index-open-network-244"></a>
+Compute Balanced System Bounds for single-class, open Queueing Networks
+with K service centers.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>overall arrival rate to the system (scalar). Abort if
+<var>lambda</var><code> &lt; 0 </code>
+
+          <br><dt><var>D</var><dd><var>D</var><code>(k)</code> is the service demand at center k. 
+The service demand vector <var>D</var> must be nonempty, and all demands
+must be nonnegative (<var>D</var><code>(k) &ge; 0</code> for all k).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dd>Lower bound on the system throughput.
+
+          <br><dt><var>Rl</var><dt><var>Ru</var><dd>Lower and upper bound on the system response time.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnopenab.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Edward D. Lazowska, John Zahorjan, G.  Scott Graham, and Kenneth
+C. Sevcik, <cite>Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models</cite>, Prentice Hall,
+1984. <a href="http://www.cs.washington.edu/homes/lazowska/qsp/">http://www.cs.washington.edu/homes/lazowska/qsp/</a>. In
+particular, see section 5.4 ("Balanced Systems Bounds").
+
+   <p><a name="index-Lazowska_002c-E_002e-D_002e-245"></a><a name="index-Zahorjan_002c-J_002e-246"></a><a name="index-Graham_002c-G_002e-S_002e-247"></a><a name="index-Sevcik_002c-K_002e-C_002e-248"></a>
+<a name="doc_002dqnclosedbsb"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnclosedbsb</b> (<var>N, D</var>)<var><a name="index-qnclosedbsb-249"></a></var><br>
+&mdash; Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnclosedbsb</b> (<var>N, D, Z</var>)<var><a name="index-qnclosedbsb-250"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-balanced-system-251"></a><a name="index-closed-network-252"></a>
+Compute Balanced System Bounds for single-class, closed Queueing Networks
+with K service centers.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>number of requests in the system (scalar).
+
+          <br><dt><var>D</var><dd><var>D</var><code>(k)</code> is the service demand at center k;
+<var>K</var><code>(k) &ge; 0</code>.
+
+          <br><dt><var>Z</var><dd>external delay (think time, scalar, <var>Z</var><code> &ge; 0</code>). If
+omitted, it is assumed to be zero.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dt><var>Xu</var><dd>Lower and upper bound on the system throughput.
+
+          <br><dt><var>Rl</var><dt><var>Ru</var><dd>Lower and upper bound on the system response time.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedab, qnclosedgb, qnclosedpb.
+
+        </blockquote></div>
+
+   <p><a name="doc_002dqnclosedpb"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>Xl</var>, <var>Xu</var>] = <b>qnclosedpb</b> (<var>N, D </var>)<var><a name="index-qnclosedpb-253"></a></var><br>
+<blockquote>
+        <p>Compute PB Bounds (C. H. Hsieh and S. Lam, 1987)
+for single-class, closed Queueing Networks
+with K service centers.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>number of requests in the system (scalar). Must be <var>N</var><code> &gt; 0</code>.
+
+          <br><dt><var>D</var><dd><var>D</var><code>(k)</code> is the service demand of service center k. Must be
+<var>D</var><code>(k) &ge; 0</code> for all k.
+
+          <br><dt><var>Z</var><dd>external delay (think time, scalar). If omitted, it is assumed to be zero. 
+Must be <var>Z</var><code> &ge; 0</code>.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dt><var>Xu</var><dd>Lower and upper bounds on the system throughput.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedab, qbclosedbsb, qnclosedgb.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>The original paper describing PB Bounds is C. H. Hsieh and S. Lam,
+<cite>Two classes of performance bounds for closed queueing networks</cite>,
+PEVA, vol. 7, n. 1, pp. 3&ndash;30, 1987
+
+   <p>This function implements the non-iterative variant described in G. 
+Casale, R. R. Muntz, G. Serazzi, <cite>Geometric Bounds: a
+Non-Iterative Analysis Technique for Closed Queueing Networks</cite>, IEEE
+Transactions on Computers, 57(6):780-794, June 2008.
+
+   <p><a name="index-Hsieh_002c-C_002e-H-254"></a><a name="index-Lam_002c-S_002e-255"></a><a name="index-Casale_002c-G_002e-256"></a><a name="index-Muntz_002c-R_002e-R_002e-257"></a><a name="index-Serazzi_002c-G_002e-258"></a>
+<a name="doc_002dqnclosedgb"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>Xl</var>, <var>Xu</var>, <var>Ql</var>, <var>Qu</var>] = <b>qnclosedgb</b> (<var>N, D, Z</var>)<var><a name="index-qnclosedgb-259"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-geometric-260"></a><a name="index-closed-network-261"></a>
+Compute Geometric Bounds (GB) for single-class, closed Queueing Networks.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>number of requests in the system (scalar, <var>N</var><code> &gt; 0</code>).
+
+          <br><dt><var>D</var><dd><var>D</var><code>(k)</code> is the service demand of service center k
+(<var>D</var><code>(k) &ge; 0</code>).
+
+          <br><dt><var>Z</var><dd>external delay (think time, scalar). If omitted, it is assumed to be zero.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dt><var>Xu</var><dd>Lower and upper bound on the system throughput. If <var>Z</var><code>&gt;0</code>,
+these bounds are computed using <em>Geometric Square-root Bounds</em>
+(GSB). If <var>Z</var><code>==0</code>, these bounds are computed using <em>Geometric Bounds</em> (GB)
+
+          <br><dt><var>Ql</var><dt><var>Qu</var><dd><var>Ql</var><code>(i)</code> and <var>Qu</var><code>(i)</code> are the lower and upper
+bounds respectively of the queue length for service center i.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedab.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>G. Casale, R. R. Muntz, G. Serazzi,
+<cite>Geometric Bounds: a Non-Iterative Analysis Technique for Closed
+Queueing Networks</cite>, IEEE Transactions on Computers, 57(6):780-794,
+June 2008. <a href="http://doi.ieeecomputersociety.org/10.1109/TC.2008.37">http://doi.ieeecomputersociety.org/10.1109/TC.2008.37</a>
+
+   <p><a name="index-Casale_002c-G_002e-262"></a><a name="index-Muntz_002c-R_002e-R_002e-263"></a><a name="index-Serazzi_002c-G_002e-264"></a>
+In this implementation we set X^+ and X^- as the upper
+and lower Asymptotic Bounds as computed by the <code>qnclosedab</code>
+function, respectively.
+
+<div class="node">
+<a name="Utility-functions"></a>
+<p><hr>
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Bounds-on-performance">Bounds on performance</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">6.6 Utility functions</h3>
+
+<h4 class="subsection">6.6.1 Open or closed networks</h4>
+
+<p><a name="doc_002dqnclosed"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnclosed</b> (<var>N, S, V, <small class="dots">...</small></var>)<var><a name="index-qnclosed-265"></a></var><br>
+<blockquote>
+        <p><a name="index-closed-network-266"></a>
+This function computes steady-state performance measures of closed
+queueing networks using the Mean Value Analysis (MVA) algorithm. The
+qneneing network is allowed to contain fixed-capacity centers, delay
+centers or general load-dependent centers. Multiple request
+classes are supported.
+
+        <p>This function dispatches the computation to one of
+<code>qnclosedsinglemva</code>, <code>qnclosedsinglemvald</code> or
+<code>qnclosedmultimva</code>.
+
+          <ul>
+<li>If <var>N</var> is a scalar, the network is assumed to have a single
+class of requests; in this case, the exact MVA algorithm is used to
+analyze the network. If <var>S</var> is a vector, then <var>S</var><code>(k)</code>
+is the average service time of center k, and this function
+calls <code>qnclosedsinglemva</code> which supports load-independent
+service centers. If <var>S</var> is a matrix, <var>S</var><code>(k,i)</code> is the
+average service time at service center k when i &ge;
+1 jobs are present; in this case, the network is analyzed with the
+<code>qnclosedsinglemvald</code> function.
+
+          <li>If <var>N</var> is a vector, the network is assumed to have multiple
+classes of requests, and is analyzed using the exact multiclass
+MVA algorithm as implemented in the <code>qnclosedmultimva</code> function.
+
+        </ul>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedsinglemva, qnclosedsinglemvald, qnclosedmultimva.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+<pre class="example"><pre class="verbatim">      P = [0 0.3 0.7; 1 0 0; 1 0 0]; # Transition probability matrix
+      S = [1 0.6 0.2]; # Average service times
+      m = ones(1,3); # All centers are single-server
+      Z = 2; # External delay
+      N = 15; # Maximum population to consider
+     
+      V = qnvisits(P); # Compute number of visits from P
+      D = V .* S; # Compute service demand from S and V
+      X_bsb_lower = X_bsb_upper = zeros(1,N);
+      X_ab_lower = X_ab_upper = zeros(1,N);
+      X_mva = zeros(1,N);
+      for n=1:N
+        [X_bsb_lower(n) X_bsb_upper(n)] = qnclosedbsb(n, D, Z);
+        [X_ab_lower(n) X_ab_upper(n)] = qnclosedab(n, D, Z);
+        [U R Q X] = qnclosed( n, S, V, m, Z );
+        X_mva(n) = X(1)/V(1);
+      endfor
+      close all;
+      plot(1:N, X_ab_lower,"g;Asymptotic Bounds;", \
+           1:N, X_bsb_lower,"k;Balanced System Bounds;", \
+           1:N, X_mva,"b;MVA;", "linewidth", 2, \
+           1:N, X_bsb_upper,"k", \
+           1:N, X_ab_upper,"g" );
+      axis([1,N,0,1]);
+      xlabel("Number of Requests n");
+      ylabel("System Throughput X(n)");
+      legend("location","southeast");</pre></pre>
+   <p><a name="doc_002dqnopen"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnopen</b> (<var>lambda, S, V, <small class="dots">...</small></var>)<var><a name="index-qnopen-267"></a></var><br>
+<blockquote>
+        <p><a name="index-open-network-268"></a>
+Compute utilization, response time, average number of requests in the
+system, and throughput for open queueing networks. If <var>lambda</var> is
+a scalar, the network is considered a single-class QN and is solved
+using <code>qnopensingle</code>. If <var>lambda</var> is a vector, the network
+is considered as a multiclass QN and solved using <code>qnopenmulti</code>.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnopensingle, qnopenmulti.
+
+        </blockquote></div>
+
+<!-- Compute the visit counts -->
+<h4 class="subsection">6.6.2 Computation of the visit counts</h4>
+
+<p>For single-class networks the average number of visits satisfy the
+following equation:
+
+<pre class="example">     V == P0 + V*P;
+</pre>
+   <p class="noindent">where P_0 j is the probability that an external
+arrival goes to service center j. If \lambda_j is the
+external arrival rate to service center j, and \lambda =
+\sum_j \lambda_j is the overall external arrival rate, then
+P_0 j = \lambda_j / \lambda.
+
+   <p>For closed networks, the visit ratios satisfy the following equation:
+
+<pre class="example">     V(1) == 1 &amp;&amp; V == V*P;
+</pre>
+   <p>The definitions above can be extended to multiple class networks as
+follows. We define the visit ratios V_sj for class s
+customers at service center j as follows:
+
+   <p>V_sj = sum_r sum_i V_ri P_risj, for all s,j
+V_s1 = 1, for all s
+
+<p class="noindent">while for open networks:
+
+   <p>V_sj = P_0sj + sum_r sum_i V_ri P_risj, for all s,j
+
+<p class="noindent">where P_0sj is the probability that an external
+arrival goes to service center j as a class-s request. 
+If \lambda_sj is the external arrival rate of class s
+requests to service center j, and \lambda = \sum_s \sum_j
+\lambda_sj is the overall external arrival rate to the whole system,
+then P_0sj = \lambda_sj / \lambda.
+
+   <p><a name="doc_002dqnvisits"></a>
+
+<div class="defun">
+&mdash; Function File: [<var>V</var> <var>ch</var>] = <b>qnvisits</b> (<var>P</var>)<var><a name="index-qnvisits-269"></a></var><br>
+&mdash; Function File: <var>V</var> = <b>qnvisits</b> (<var>P, lambda</var>)<var><a name="index-qnvisits-270"></a></var><br>
+<blockquote>
+        <p>Compute the average number of visits to the service centers of a
+single class, open or closed Queueing Network with N service
+centers.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd>Routing probability matrix. For single class networks,
+<var>P</var><code>(i,j)</code> is the probability that a request which completed
+service at center i is routed to center j. For closed
+networks it must hold that <code>sum(</code><var>P</var><code>,2)==1</code>. The routing
+graph myst be strongly connected, meaning that it must be possible to
+eventually reach each node starting from each node. For multiple
+class networks, <var>P</var><code>(r,i,s,j)</code> is the probability that a
+class r request which completed service at center i is
+routed to center j as a class s request. Class switching
+is supported.
+
+          <br><dt><var>lambda</var><dd>(open networks only) vector of external arrivals. For single class
+networks, <var>lambda</var><code>(i)</code> is the external arrival rate to
+center i. For multiple class networks,
+<var>lambda</var><code>(r,i)</code> is the arrival rate of class r
+requests to center i. If this parameter is omitted, the
+network is assumed to be closed.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>V</var><dd>For single class networks, <var>V</var><code>(i)</code> is the average number of
+visits to server i. For multiple class networks,
+<var>V</var><code>(r,i)</code> is the class r visit ratio at center
+i.
+
+          <br><dt><var>ch</var><dd>(For closed networks only). <var>ch</var><code>(c,k)</code> is the
+number of the chain that class c at center k belongs to. 
+The total number of chains is <code>max(</code><var>ch</var><code>)</code>.
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+<pre class="example"><pre class="verbatim">      P = [ 0 0.4 0.6 0; \
+            0.2 0 0.2 0.6; \
+            0 0 0 1; \
+            0 0 0 0 ];
+      lambda = [0.1 0 0 0.3];
+      V = qnvisits(P,lambda);
+      S = [2 1 2 1.8];
+      m = [3 1 1 2];
+      [U R Q X] = qnopensingle( sum(lambda), S, V, m );</pre></pre>
+   <h4 class="subsection">6.6.3 Other utility functions</h4>
+
+<p><a name="doc_002dpopulation_005fmix"></a>
+
+<div class="defun">
+&mdash; Function File: pop_mix = <b>population_mix</b> (<var>k, N</var>)<var><a name="index-population_005fmix-271"></a></var><br>
+<blockquote>
+        <p><a name="index-population-mix-272"></a><a name="index-closed-network_002c-multiple-classes-273"></a>
+Return the set of valid population mixes with exactly <var>k</var>
+customers, for a closed multiclass Queueing Network with population
+vector <var>N</var>. More specifically, given a multiclass Queueing
+Network with C customer classes, such that there are
+<var>N</var><code>(i)</code> requests of class i, a
+k-mix <var>mix</var> is a C-dimensional vector with the
+following properties:
+
+     <pre class="example">          all( mix &gt;= 0 );
+          all( mix &lt;= N );
+          sum( mix ) == k;
+</pre>
+        <p class="noindent">This function enumerates all valid k-mixes, such that
+<var>pop_mix</var><code>(i)</code> is a C dimensional row vector representing
+a valid population mix, for all i.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>k</var><dd>Total population size of the requested mix. <var>k</var> must be a nonnegative integer
+
+          <br><dt><var>N</var><dd><var>N</var><code>(i)</code> is the number of class i requests. 
+The condition <var>k</var><code> &le; sum(</code><var>N</var><code>)</code> must hold.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>pop_mix</var><dd><var>pop_mix</var><code>(i,j)</code> is the number of class j requests
+in the i-th population mix. The number of
+population mixes is <code>rows( </code><var>pop_mix</var><code> ) </code>.
+
+        </dl>
+
+        <p>Note that if you are interested in the number of k-mixes
+and you don't care to enumerate them, you can use the funcion
+<code>qnmvapop</code>.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnmvapop.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Herb Schwetman, <cite>Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models</cite>, Technical Report CSD-TR-355,
+Department of Computer Sciences, Purdue University, feb 15, 1982,
+available at
+<a href="http://www.cs.purdue.edu/research/technical_reports/1980/TR 80-355.pdf">http://www.cs.purdue.edu/research/technical_reports/1980/TR 80-355.pdf</a>
+
+   <p>Note that the slightly different problem of generating all tuples
+k_1, k_2, \ldots k_N such that \sum_i k_i = k and
+k_i are nonnegative integers, for some fixed integer k
+&ge; 0 has been described in S. Santini, <cite>Computing the
+Indices for a Complex Summation</cite>, unpublished report, available at
+<a href="http://arantxa.ii.uam.es/~ssantini/writing/notes/s668_summation.pdf">http://arantxa.ii.uam.es/~ssantini/writing/notes/s668_summation.pdf</a>
+
+   <p><a name="index-Schwetman_002c-H_002e-274"></a><a name="index-Santini_002c-S_002e-275"></a>
+<a name="doc_002dqnmvapop"></a>
+
+<div class="defun">
+&mdash; Function File: <var>H</var> = <b>qnmvapop</b> (<var>N</var>)<var><a name="index-qnmvapop-276"></a></var><br>
+<blockquote>
+        <p><a name="index-population-mix-277"></a><a name="index-closed-network_002c-multiple-classes-278"></a>
+Given a network with C customer classes, this function
+computes the number of valid population mixes <var>H</var><code>(r,n)</code> that can
+be constructed by the multiclass MVA algorithm by allocating n
+customers to the first r classes.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Population vector. <var>N</var><code>(c)</code> is the number of class-c
+requests in the system. The total number of requests in the network
+is <code>sum(</code><var>N</var><code>)</code>.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>H</var><dd><var>H</var><code>(r,n)</code> is the number of valid populations that can be
+constructed allocating n customers to the first r classes.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedmultimva,population_mix.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+   <p>Zahorjan, J. and Wong, E. <cite>The solution of separable queueing
+network models using mean value analysis</cite>. SIGMETRICS
+Perform. Eval. Rev. 10, 3 (Sep. 1981), 80-85. DOI
+<a href="http://doi.acm.org/10.1145/1010629.805477">http://doi.acm.org/10.1145/1010629.805477</a>
+
+   <p><a name="index-Zahorjan_002c-J_002e-279"></a><a name="index-Wong_002c-E_002e-280"></a>
+
+<!-- Appendix starts here -->
+<!-- DO NOT EDIT!  Generated automatically by munge-texi. -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla -->
+<!-- This file is part of the queueing toolbox, a Queueing Networks -->
+<!-- analysis package for GNU Octave. -->
+<!-- The queueing toolbox 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 3 of -->
+<!-- the License, or (at your option) any later version. -->
+<!-- The queueing toolbox 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. -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with the queueing toolbox; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Contributing-Guidelines"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Acknowledgements">Acknowledgements</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Queueing-Networks">Queueing Networks</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix A Contributing Guidelines</h2>
+
+<p>Contributions and bug reports are <em>always</em> welcome. If you want
+to contribute to the <code>queueing</code> package, here are some
+guidelines:
+
+     <ul>
+<li>If you are contributing a new function, please embed proper
+documentation within the function itself. The documentation must be in
+<code>texinfo</code> format, so that it will be extracted and formatted into
+the printable manual. See the existing functions of the
+<code>queueing</code> package for the documentation style.
+
+     <li>The documentation should be as precise as possible. In particular,
+always state what the valid ranges of the parameters are.
+
+     <li>If you are contributing a new function, ensure that the function
+properly checks the validity of its input parameters. For example,
+each function accepting vectors should check whether the dimensions
+match.
+
+     <li>Always provide bibliographic references for each algorithm you
+contribute. If your implementation differs in some way from the
+reference you give, please describe how and why your implementation
+differs.
+
+     <li>Include Octave test and demo blocks with your code. 
+Test blocks are particularly important, because Queueing Network
+algorithms tend to be quite complex to implement correctly, and we
+must ensure that the implementations provided with the
+<code>queueing</code> package are (mostly) correct.
+
+   </ul>
+
+   <p>Send your contribution to Moreno Marzolla
+(<a href="mailto:marzolla@cs.unibo.it">marzolla@cs.unibo.it</a>). Even if you are just a user of
+<code>queueing</code>, and find this package useful, let me know by
+dropping me a line. Thanks.
+
+<!-- DO NOT EDIT!  Generated automatically by munge-texi. -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla -->
+<!-- This file is part of the queueing toolbox, a Queueing Networks -->
+<!-- analysis package for GNU Octave. -->
+<!-- The queueing toolbox 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 3 of -->
+<!-- the License, or (at your option) any later version. -->
+<!-- The queueing toolbox 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. -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with the queueing toolbox; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Acknowledgements"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Copying">Copying</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Contributing-Guidelines">Contributing Guidelines</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix B Acknowledgements</h2>
+
+<p>The following people (listed in alphabetical order) contributed to the
+<code>queueing</code> package, either by providing feedback, reporting bugs
+or contributing code: Philip Carinhas, Phil Colbourn, Yves Durand,
+Marco Guazzone, Dmitry Kolesnikov.
+
+<!-- DO NOT EDIT!  Generated automatically by munge-texi. -->
+<div class="node">
+<a name="Copying"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Concept-Index">Concept Index</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Acknowledgements">Acknowledgements</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix C GNU GENERAL PUBLIC LICENSE</h2>
+
+<p><a name="index-warranty-281"></a><a name="index-copyright-282"></a>
+<div align="center">Version 3, 29 June 2007</div>
+
+<pre class="display">     Copyright &copy; 2007 Free Software Foundation, Inc. <a href="http://fsf.org/">http://fsf.org/</a>
+     
+     Everyone is permitted to copy and distribute verbatim copies of this
+     license document, but changing it is not allowed.
+</pre>
+<h3 class="heading">Preamble</h3>
+
+<p>The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+   <p>The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom
+to share and change all versions of a program&mdash;to make sure it remains
+free software for all its users.  We, the Free Software Foundation,
+use the GNU General Public License for most of our software; it
+applies also to any other work released this way by its authors.  You
+can apply it to your programs, too.
+
+   <p>When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+   <p>To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you
+have certain responsibilities if you distribute copies of the
+software, or if you modify it: responsibilities to respect the freedom
+of others.
+
+   <p>For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too,
+receive or can get the source code.  And you must show them these
+terms so they know their rights.
+
+   <p>Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+   <p>For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+   <p>Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the
+manufacturer can do so.  This is fundamentally incompatible with the
+aim of protecting users' freedom to change the software.  The
+systematic pattern of such abuse occurs in the area of products for
+individuals to use, which is precisely where it is most unacceptable. 
+Therefore, we have designed this version of the GPL to prohibit the
+practice for those products.  If such problems arise substantially in
+other domains, we stand ready to extend this provision to those
+domains in future versions of the GPL, as needed to protect the
+freedom of users.
+
+   <p>Finally, every program is threatened constantly by software patents. 
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish
+to avoid the special danger that patents applied to a free program
+could make it effectively proprietary.  To prevent this, the GPL
+assures that patents cannot be used to render the program non-free.
+
+   <p>The precise terms and conditions for copying, distribution and
+modification follow.
+
+<h3 class="heading">TERMS AND CONDITIONS</h3>
+
+     <ol type=1 start=0>
+<li>Definitions.
+
+     <p>&ldquo;This License&rdquo; refers to version 3 of the GNU General Public License.
+
+     <p>&ldquo;Copyright&rdquo; also means copyright-like laws that apply to other kinds
+of works, such as semiconductor masks.
+
+     <p>&ldquo;The Program&rdquo; refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as &ldquo;you&rdquo;.  &ldquo;Licensees&rdquo; and
+&ldquo;recipients&rdquo; may be individuals or organizations.
+
+     <p>To &ldquo;modify&rdquo; a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of
+an exact copy.  The resulting work is called a &ldquo;modified version&rdquo; of
+the earlier work or a work &ldquo;based on&rdquo; the earlier work.
+
+     <p>A &ldquo;covered work&rdquo; means either the unmodified Program or a work based
+on the Program.
+
+     <p>To &ldquo;propagate&rdquo; a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+     <p>To &ldquo;convey&rdquo; a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user
+through a computer network, with no transfer of a copy, is not
+conveying.
+
+     <p>An interactive user interface displays &ldquo;Appropriate Legal Notices&rdquo; to
+the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+     <li>Source Code.
+
+     <p>The &ldquo;source code&rdquo; for a work means the preferred form of the work for
+making modifications to it.  &ldquo;Object code&rdquo; means any non-source form
+of a work.
+
+     <p>A &ldquo;Standard Interface&rdquo; means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+     <p>The &ldquo;System Libraries&rdquo; of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+&ldquo;Major Component&rdquo;, in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+     <p>The &ldquo;Corresponding Source&rdquo; for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+     <p>The Corresponding Source need not include anything that users can
+regenerate automatically from other parts of the Corresponding Source.
+
+     <p>The Corresponding Source for a work in source code form is that same
+work.
+
+     <li>Basic Permissions.
+
+     <p>All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+     <p>You may make, run and propagate covered works that you do not convey,
+without conditions so long as your license otherwise remains in force. 
+You may convey covered works to others for the sole purpose of having
+them make modifications exclusively for you, or provide you with
+facilities for running those works, provided that you comply with the
+terms of this License in conveying all material for which you do not
+control copyright.  Those thus making or running the covered works for
+you must do so exclusively on your behalf, under your direction and
+control, on terms that prohibit them from making any copies of your
+copyrighted material outside their relationship with you.
+
+     <p>Conveying under any other circumstances is permitted solely under the
+conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+     <li>Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+     <p>No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+     <p>When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such
+circumvention is effected by exercising rights under this License with
+respect to the covered work, and you disclaim any intention to limit
+operation or modification of the work as a means of enforcing, against
+the work's users, your or third parties' legal rights to forbid
+circumvention of technological measures.
+
+     <li>Conveying Verbatim Copies.
+
+     <p>You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+     <p>You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+     <li>Conveying Modified Source Versions.
+
+     <p>You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these
+conditions:
+
+          <ol type=a start=1>
+<li>The work must carry prominent notices stating that you modified it,
+and giving a relevant date.
+
+          <li>The work must carry prominent notices stating that it is released
+under this License and any conditions added under section 7.  This
+requirement modifies the requirement in section 4 to &ldquo;keep intact all
+notices&rdquo;.
+
+          <li>You must license the entire work, as a whole, under this License to
+anyone who comes into possession of a copy.  This License will
+therefore apply, along with any applicable section 7 additional terms,
+to the whole of the work, and all its parts, regardless of how they
+are packaged.  This License gives no permission to license the work in
+any other way, but it does not invalidate such permission if you have
+separately received it.
+
+          <li>If the work has interactive user interfaces, each must display
+Appropriate Legal Notices; however, if the Program has interactive
+interfaces that do not display Appropriate Legal Notices, your work
+need not make them do so.
+          </ol>
+
+     <p>A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+&ldquo;aggregate&rdquo; if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+     <li>Conveying Non-Source Forms.
+
+     <p>You may convey a covered work in object code form under the terms of
+sections 4 and 5, provided that you also convey the machine-readable
+Corresponding Source under the terms of this License, in one of these
+ways:
+
+          <ol type=a start=1>
+<li>Convey the object code in, or embodied in, a physical product
+(including a physical distribution medium), accompanied by the
+Corresponding Source fixed on a durable physical medium customarily
+used for software interchange.
+
+          <li>Convey the object code in, or embodied in, a physical product
+(including a physical distribution medium), accompanied by a written
+offer, valid for at least three years and valid for as long as you
+offer spare parts or customer support for that product model, to give
+anyone who possesses the object code either (1) a copy of the
+Corresponding Source for all the software in the product that is
+covered by this License, on a durable physical medium customarily used
+for software interchange, for a price no more than your reasonable
+cost of physically performing this conveying of source, or (2) access
+to copy the Corresponding Source from a network server at no charge.
+
+          <li>Convey individual copies of the object code with a copy of the written
+offer to provide the Corresponding Source.  This alternative is
+allowed only occasionally and noncommercially, and only if you
+received the object code with such an offer, in accord with subsection
+6b.
+
+          <li>Convey the object code by offering access from a designated place
+(gratis or for a charge), and offer equivalent access to the
+Corresponding Source in the same way through the same place at no
+further charge.  You need not require recipients to copy the
+Corresponding Source along with the object code.  If the place to copy
+the object code is a network server, the Corresponding Source may be
+on a different server (operated by you or a third party) that supports
+equivalent copying facilities, provided you maintain clear directions
+next to the object code saying where to find the Corresponding Source. 
+Regardless of what server hosts the Corresponding Source, you remain
+obligated to ensure that it is available for as long as needed to
+satisfy these requirements.
+
+          <li>Convey the object code using peer-to-peer transmission, provided you
+inform other peers where the object code and Corresponding Source of
+the work are being offered to the general public at no charge under
+subsection 6d.
+
+          </ol>
+
+     <p>A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+     <p>A &ldquo;User Product&rdquo; is either (1) a &ldquo;consumer product&rdquo;, which means any
+tangible personal property which is normally used for personal,
+family, or household purposes, or (2) anything designed or sold for
+incorporation into a dwelling.  In determining whether a product is a
+consumer product, doubtful cases shall be resolved in favor of
+coverage.  For a particular product received by a particular user,
+&ldquo;normally used&rdquo; refers to a typical or common use of that class of
+product, regardless of the status of the particular user or of the way
+in which the particular user actually uses, or expects or is expected
+to use, the product.  A product is a consumer product regardless of
+whether the product has substantial commercial, industrial or
+non-consumer uses, unless such uses represent the only significant
+mode of use of the product.
+
+     <p>&ldquo;Installation Information&rdquo; for a User Product means any methods,
+procedures, authorization keys, or other information required to
+install and execute modified versions of a covered work in that User
+Product from a modified version of its Corresponding Source.  The
+information must suffice to ensure that the continued functioning of
+the modified object code is in no case prevented or interfered with
+solely because modification has been made.
+
+     <p>If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+     <p>The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or
+updates for a work that has been modified or installed by the
+recipient, or for the User Product in which it has been modified or
+installed.  Access to a network may be denied when the modification
+itself materially and adversely affects the operation of the network
+or violates the rules and protocols for communication across the
+network.
+
+     <p>Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+     <li>Additional Terms.
+
+     <p>&ldquo;Additional permissions&rdquo; are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions. 
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+     <p>When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+     <p>Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders
+of that material) supplement the terms of this License with terms:
+
+          <ol type=a start=1>
+<li>Disclaiming warranty or limiting liability differently from the terms
+of sections 15 and 16 of this License; or
+
+          <li>Requiring preservation of specified reasonable legal notices or author
+attributions in that material or in the Appropriate Legal Notices
+displayed by works containing it; or
+
+          <li>Prohibiting misrepresentation of the origin of that material, or
+requiring that modified versions of such material be marked in
+reasonable ways as different from the original version; or
+
+          <li>Limiting the use for publicity purposes of names of licensors or
+authors of the material; or
+
+          <li>Declining to grant rights under trademark law for use of some trade
+names, trademarks, or service marks; or
+
+          <li>Requiring indemnification of licensors and authors of that material by
+anyone who conveys the material (or modified versions of it) with
+contractual assumptions of liability to the recipient, for any
+liability that these contractual assumptions directly impose on those
+licensors and authors.
+          </ol>
+
+     <p>All other non-permissive additional terms are considered &ldquo;further
+restrictions&rdquo; within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+     <p>If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+     <p>Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions; the
+above requirements apply either way.
+
+     <li>Termination.
+
+     <p>You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+     <p>However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+     <p>Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+     <p>Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+     <li>Acceptance Not Required for Having Copies.
+
+     <p>You are not required to accept this License in order to receive or run
+a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+     <li>Automatic Licensing of Downstream Recipients.
+
+     <p>Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+     <p>An &ldquo;entity transaction&rdquo; is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+     <p>You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+     <li>Patents.
+
+     <p>A &ldquo;contributor&rdquo; is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's &ldquo;contributor version&rdquo;.
+
+     <p>A contributor's &ldquo;essential patent claims&rdquo; are all patent claims owned
+or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, &ldquo;control&rdquo; includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+     <p>Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+     <p>In the following three paragraphs, a &ldquo;patent license&rdquo; is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To &ldquo;grant&rdquo; such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+     <p>If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  &ldquo;Knowingly relying&rdquo; means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+     <p>If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+     <p>A patent license is &ldquo;discriminatory&rdquo; if it does not include within the
+scope of its coverage, prohibits the exercise of, or is conditioned on
+the non-exercise of one or more of the rights that are specifically
+granted under this License.  You may not convey a covered work if you
+are a party to an arrangement with a third party that is in the
+business of distributing software, under which you make payment to the
+third party based on the extent of your activity of conveying the
+work, and under which the third party grants, to any of the parties
+who would receive the covered work from you, a discriminatory patent
+license (a) in connection with copies of the covered work conveyed by
+you (or copies made from those copies), or (b) primarily for and in
+connection with specific products or compilations that contain the
+covered work, unless you entered into that arrangement, or that patent
+license was granted, prior to 28 March 2007.
+
+     <p>Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+     <li>No Surrender of Others' Freedom.
+
+     <p>If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey
+a covered work so as to satisfy simultaneously your obligations under
+this License and any other pertinent obligations, then as a
+consequence you may not convey it at all.  For example, if you agree
+to terms that obligate you to collect a royalty for further conveying
+from those to whom you convey the Program, the only way you could
+satisfy both those terms and this License would be to refrain entirely
+from conveying the Program.
+
+     <li>Use with the GNU Affero General Public License.
+
+     <p>Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+     <li>Revised Versions of this License.
+
+     <p>The Free Software Foundation may publish revised and/or new versions
+of the GNU General Public License from time to time.  Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+     <p>Each version is given a distinguishing version number.  If the Program
+specifies that a certain numbered version of the GNU General Public
+License &ldquo;or any later version&rdquo; applies to it, you have the option of
+following the terms and conditions either of that numbered version or
+of any later version published by the Free Software Foundation.  If
+the Program does not specify a version number of the GNU General
+Public License, you may choose any version ever published by the Free
+Software Foundation.
+
+     <p>If the Program specifies that a proxy can decide which future versions
+of the GNU General Public License can be used, that proxy's public
+statement of acceptance of a version permanently authorizes you to
+choose that version for the Program.
+
+     <p>Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+     <li>Disclaimer of Warranty.
+
+     <p>THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM &ldquo;AS IS&rdquo; WITHOUT
+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+     <li>Limitation of Liability.
+
+     <p>IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
+CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
+NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
+LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
+TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
+PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+     <li>Interpretation of Sections 15 and 16.
+
+     <p>If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+     </ol>
+
+<h3 class="heading">END OF TERMS AND CONDITIONS</h3>
+
+<h3 class="heading">How to Apply These Terms to Your New Programs</h3>
+
+<p>If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+   <p>To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the &ldquo;copyright&rdquo; line and a pointer to where the full notice is found.
+
+<pre class="smallexample">     <var>one line to give the program's name and a brief idea of what it does.</var>
+     Copyright (C) <var>year</var> <var>name of author</var>
+     
+     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 3 of the License, or (at
+     your option) any later version.
+     
+     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.
+     
+     You should have received a copy of the GNU General Public License
+     along with this program.  If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
+</pre>
+   <p>Also add information on how to contact you by electronic and paper mail.
+
+   <p>If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+<pre class="smallexample">     <var>program</var> Copyright (C) <var>year</var> <var>name of author</var>
+     This program comes with ABSOLUTELY NO WARRANTY; for details type &lsquo;<samp><span class="samp">show w</span></samp>&rsquo;.
+     This is free software, and you are welcome to redistribute it
+     under certain conditions; type &lsquo;<samp><span class="samp">show c</span></samp>&rsquo; for details.
+</pre>
+   <p>The hypothetical commands &lsquo;<samp><span class="samp">show w</span></samp>&rsquo; and &lsquo;<samp><span class="samp">show c</span></samp>&rsquo; should show
+the appropriate parts of the General Public License.  Of course, your
+program's commands might be different; for a GUI interface, you would
+use an &ldquo;about box&rdquo;.
+
+   <p>You should also get your employer (if you work as a programmer) or school,
+if any, to sign a &ldquo;copyright disclaimer&rdquo; for the program, if necessary. 
+For more information on this, and how to apply and follow the GNU GPL, see
+<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
+
+   <p>The GNU General Public License does not permit incorporating your
+program into proprietary programs.  If your program is a subroutine
+library, you may consider it more useful to permit linking proprietary
+applications with the library.  If this is what you want to do, use
+the GNU Lesser General Public License instead of this License.  But
+first, please read <a href="http://www.gnu.org/philosophy/why-not-lgpl.html">http://www.gnu.org/philosophy/why-not-lgpl.html</a>.
+
+<!-- INDEX -->
+<div class="node">
+<a name="Concept-Index"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Function-Index">Function Index</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Copying">Copying</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="unnumbered">Concept Index</h2>
+
+<ul class="index-cp" compact>
+<li><a href="#index-Approximate-MVA-166">Approximate MVA</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Asymmetric-_0040math_007bM_002fM_002fm_007d-system-66">Asymmetric M/M/m system</a>: <a href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a></li>
+<li><a href="#index-BCMP-network-121">BCMP network</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Birth_002ddeath-process-19">Birth-death process</a>: <a href="#Birth_002dDeath-process">Birth-Death process</a></li>
+<li><a href="#index-blocking-queueing-network-216">blocking queueing network</a>: <a href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a></li>
+<li><a href="#index-bounds_002c-asymptotic-228">bounds, asymptotic</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-bounds_002c-balanced-system-243">bounds, balanced system</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-bounds_002c-geometric-260">bounds, geometric</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-closed-network-266">closed network</a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-closed-network-237">closed network</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-closed-network-98">closed network</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Closed-network_002c-approximate-analysis-168">Closed network, approximate analysis</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-closed-network_002c-finite-capacity-217">closed network, finite capacity</a>: <a href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a></li>
+<li><a href="#index-closed-network_002c-multiple-classes-273">closed network, multiple classes</a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-closed-network_002c-multiple-classes-223">closed network, multiple classes</a>: <a href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a></li>
+<li><a href="#index-Closed-network_002c-multiple-classes-198">Closed network, multiple classes</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-closed-network_002c-multiple-classes-180">closed network, multiple classes</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Closed-network_002c-single-class-167">Closed network, single class</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-closed-network_002c-single-class-137">closed network, single class</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-CMVA-158">CMVA</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Continuous-time-Markov-chain-14">Continuous time Markov chain</a>: <a href="#CTMC-Stationary-Probability">CTMC Stationary Probability</a></li>
+<li><a href="#index-convolution-algorithm-100">convolution algorithm</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-copyright-282">copyright</a>: <a href="#Copying">Copying</a></li>
+<li><a href="#index-Discrete-time-Markov-chain-4">Discrete time Markov chain</a>: <a href="#DTMC-Stationary-Probability">DTMC Stationary Probability</a></li>
+<li><a href="#index-Expected-sojourn-time-22">Expected sojourn time</a>: <a href="#Expected-Sojourn-Time">Expected Sojourn Time</a></li>
+<li><a href="#index-First-passage-times-36">First passage times</a>: <a href="#CTMC-First-Passage-Times">CTMC First Passage Times</a></li>
+<li><a href="#index-First-passage-times-10">First passage times</a>: <a href="#DTMC-First-Passage-Times">DTMC First Passage Times</a></li>
+<li><a href="#index-Jackson-network-91">Jackson network</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-load_002ddependent-service-center-110">load-dependent service center</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-g_t_0040math_007bM_002fG_002f1_007d-system-72">M/G/1 system</a>: <a href="#The-M_002fG_002f1-System">The M/G/1 System</a></li>
+<li><a href="#index-g_t_0040math_007bM_002fH_005fm_002f1_007d-system-74">M/H_m/1 system</a>: <a href="#The-M_002fHm_002f1-System">The M/Hm/1 System</a></li>
+<li><a href="#index-g_t_0040math_007bM_002fM_002f1_007d-system-38">M/M/1 system</a>: <a href="#The-M_002fM_002f1-System">The M/M/1 System</a></li>
+<li><a href="#index-g_t_0040math_007bM_002fM_002f1_002fK_007d-system-58">M/M/1/K system</a>: <a href="#The-M_002fM_002f1_002fK-System">The M/M/1/K System</a></li>
+<li><a href="#index-g_t_0040math_007bM_002fM_002f_007dinf-system-51">M/M/inf system</a>: <a href="#The-M_002fM_002finf-System">The M/M/inf System</a></li>
+<li><a href="#index-g_t_0040math_007bM_002fM_002fm_007d-system-45">M/M/m system</a>: <a href="#The-M_002fM_002fm-System">The M/M/m System</a></li>
+<li><a href="#index-g_t_0040math_007bM_002fM_002fm_002fK_007d-system-60">M/M/m/K system</a>: <a href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-35">Markov chain, continuous time</a>: <a href="#CTMC-First-Passage-Times">CTMC First Passage Times</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-27">Markov chain, continuous time</a>: <a href="#Expected-Time-to-Absorption">Expected Time to Absorption</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-24">Markov chain, continuous time</a>: <a href="#Time_002dAveraged-Expected-Sojourn-Time">Time-Averaged Expected Sojourn Time</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-21">Markov chain, continuous time</a>: <a href="#Expected-Sojourn-Time">Expected Sojourn Time</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-18">Markov chain, continuous time</a>: <a href="#Birth_002dDeath-process">Birth-Death process</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-13">Markov chain, continuous time</a>: <a href="#CTMC-Stationary-Probability">CTMC Stationary Probability</a></li>
+<li><a href="#index-Markov-chain_002c-discrete-time-9">Markov chain, discrete time</a>: <a href="#DTMC-First-Passage-Times">DTMC First Passage Times</a></li>
+<li><a href="#index-Markov-chain_002c-discrete-time-3">Markov chain, discrete time</a>: <a href="#DTMC-Stationary-Probability">DTMC Stationary Probability</a></li>
+<li><a href="#index-Markov-chain_002c-state-occupancy-probabilities-15">Markov chain, state occupancy probabilities</a>: <a href="#CTMC-Stationary-Probability">CTMC Stationary Probability</a></li>
+<li><a href="#index-Markov-chain_002c-stationary-probabilities-5">Markov chain, stationary probabilities</a>: <a href="#DTMC-Stationary-Probability">DTMC Stationary Probability</a></li>
+<li><a href="#index-Mean-time-to-absorption-28">Mean time to absorption</a>: <a href="#Expected-Time-to-Absorption">Expected Time to Absorption</a></li>
+<li><a href="#index-Mean-Value-Analysys-_0028MVA_0029-136">Mean Value Analysys (MVA)</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Mean-Value-Analysys-_0028MVA_0029_002c-approximate-165">Mean Value Analysys (MVA), approximate</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-mixed-network-208">mixed network</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-normalization-constant-99">normalization constant</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-open-network-268">open network</a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-open-network-229">open network</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-open-network_002c-multiple-classes-128">open network, multiple classes</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-open-network_002c-single-class-90">open network, single class</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-population-mix-272">population mix</a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-queueing-network-with-blocking-215">queueing network with blocking</a>: <a href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a></li>
+<li><a href="#index-queueing-networks-75">queueing networks</a>: <a href="#Queueing-Networks">Queueing Networks</a></li>
+<li><a href="#index-RS-blocking-226">RS blocking</a>: <a href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a></li>
+<li><a href="#index-Stationary-probabilities-16">Stationary probabilities</a>: <a href="#CTMC-Stationary-Probability">CTMC Stationary Probability</a></li>
+<li><a href="#index-Stationary-probabilities-6">Stationary probabilities</a>: <a href="#DTMC-Stationary-Probability">DTMC Stationary Probability</a></li>
+<li><a href="#index-Time_002dalveraged-sojourn-time-25">Time-alveraged sojourn time</a>: <a href="#Time_002dAveraged-Expected-Sojourn-Time">Time-Averaged Expected Sojourn Time</a></li>
+<li><a href="#index-traffic-intensity-52">traffic intensity</a>: <a href="#The-M_002fM_002finf-System">The M/M/inf System</a></li>
+<li><a href="#index-warranty-281">warranty</a>: <a href="#Copying">Copying</a></li>
+   </ul><div class="node">
+<a name="Function-Index"></a>
+<p><hr>
+Next:&nbsp;<a rel="next" accesskey="n" href="#Author-Index">Author Index</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Concept-Index">Concept Index</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="unnumbered">Function Index</h2>
+
+
+
+<ul class="index-fn" compact>
+<li><a href="#index-ctmc-11"><code>ctmc</code></a>: <a href="#CTMC-Stationary-Probability">CTMC Stationary Probability</a></li>
+<li><a href="#index-ctmc_005fbd-17"><code>ctmc_bd</code></a>: <a href="#Birth_002dDeath-process">Birth-Death process</a></li>
+<li><a href="#index-ctmc_005fexps-20"><code>ctmc_exps</code></a>: <a href="#Expected-Sojourn-Time">Expected Sojourn Time</a></li>
+<li><a href="#index-ctmc_005ffpt-33"><code>ctmc_fpt</code></a>: <a href="#CTMC-First-Passage-Times">CTMC First Passage Times</a></li>
+<li><a href="#index-ctmc_005fmtta-26"><code>ctmc_mtta</code></a>: <a href="#Expected-Time-to-Absorption">Expected Time to Absorption</a></li>
+<li><a href="#index-ctmc_005ftaexps-23"><code>ctmc_taexps</code></a>: <a href="#Time_002dAveraged-Expected-Sojourn-Time">Time-Averaged Expected Sojourn Time</a></li>
+<li><a href="#index-dtmc-1"><code>dtmc</code></a>: <a href="#DTMC-Stationary-Probability">DTMC Stationary Probability</a></li>
+<li><a href="#index-dtmc_005ffpt-7"><code>dtmc_fpt</code></a>: <a href="#DTMC-First-Passage-Times">DTMC First Passage Times</a></li>
+<li><a href="#index-population_005fmix-271"><code>population_mix</code></a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-qnammm-65"><code>qnammm</code></a>: <a href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a></li>
+<li><a href="#index-qnclosed-265"><code>qnclosed</code></a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-qnclosedab-234"><code>qnclosedab</code></a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-qnclosedbsb-249"><code>qnclosedbsb</code></a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-qnclosedgb-259"><code>qnclosedgb</code></a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-qnclosedmultimva-173"><code>qnclosedmultimva</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnclosedmultimvaapprox-191"><code>qnclosedmultimvaapprox</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnclosedpb-253"><code>qnclosedpb</code></a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-qnclosedsinglemva-133"><code>qnclosedsinglemva</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnclosedsinglemvaapprox-160"><code>qnclosedsinglemvaapprox</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnclosedsinglemvald-146"><code>qnclosedsinglemvald</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qncmva-155"><code>qncmva</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnconvolution-96"><code>qnconvolution</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnconvolutionld-106"><code>qnconvolutionld</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnjackson-87"><code>qnjackson</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnmarkov-219"><code>qnmarkov</code></a>: <a href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a></li>
+<li><a href="#index-qnmg1-71"><code>qnmg1</code></a>: <a href="#The-M_002fG_002f1-System">The M/G/1 System</a></li>
+<li><a href="#index-qnmh1-73"><code>qnmh1</code></a>: <a href="#The-M_002fHm_002f1-System">The M/Hm/1 System</a></li>
+<li><a href="#index-qnmix-206"><code>qnmix</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnmknode-76"><code>qnmknode</code></a>: <a href="#Generic-Algorithms">Generic Algorithms</a></li>
+<li><a href="#index-qnmm1-37"><code>qnmm1</code></a>: <a href="#The-M_002fM_002f1-System">The M/M/1 System</a></li>
+<li><a href="#index-qnmm1k-57"><code>qnmm1k</code></a>: <a href="#The-M_002fM_002f1_002fK-System">The M/M/1/K System</a></li>
+<li><a href="#index-qnmminf-50"><code>qnmminf</code></a>: <a href="#The-M_002fM_002finf-System">The M/M/inf System</a></li>
+<li><a href="#index-qnmmm-43"><code>qnmmm</code></a>: <a href="#The-M_002fM_002fm-System">The M/M/m System</a></li>
+<li><a href="#index-qnmmmk-59"><code>qnmmmk</code></a>: <a href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a></li>
+<li><a href="#index-qnmvablo-214"><code>qnmvablo</code></a>: <a href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a></li>
+<li><a href="#index-qnmvapop-276"><code>qnmvapop</code></a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-qnopen-267"><code>qnopen</code></a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-qnopenab-227"><code>qnopenab</code></a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-qnopenbsb-242"><code>qnopenbsb</code></a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-qnopenmulti-126"><code>qnopenmulti</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnopensingle-118"><code>qnopensingle</code></a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-qnsolve-83"><code>qnsolve</code></a>: <a href="#Generic-Algorithms">Generic Algorithms</a></li>
+<li><a href="#index-qnvisits-269"><code>qnvisits</code></a>: <a href="#Utility-functions">Utility functions</a></li>
+   </ul><div class="node">
+<a name="Author-Index"></a>
+<p><hr>
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#Function-Index">Function Index</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="unnumbered">Author Index</h2>
+
+
+
+<ul class="index-au" compact>
+<li><a href="#index-Akyildiz_002c-I_002e-F_002e-218">Akyildiz, I. F.</a>: <a href="#Algorithms-for-non-Product_002dform-QNs">Algorithms for non Product-form QNs</a></li>
+<li><a href="#index-Bard_002c-Y_002e-200">Bard, Y.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Bolch_002c-G_002e-92">Bolch, G.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Bolch_002c-G_002e-67">Bolch, G.</a>: <a href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a></li>
+<li><a href="#index-Bolch_002c-G_002e-61">Bolch, G.</a>: <a href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a></li>
+<li><a href="#index-Bolch_002c-G_002e-53">Bolch, G.</a>: <a href="#The-M_002fM_002finf-System">The M/M/inf System</a></li>
+<li><a href="#index-Bolch_002c-G_002e-46">Bolch, G.</a>: <a href="#The-M_002fM_002fm-System">The M/M/m System</a></li>
+<li><a href="#index-Bolch_002c-G_002e-39">Bolch, G.</a>: <a href="#The-M_002fM_002f1-System">The M/M/1 System</a></li>
+<li><a href="#index-Bolch_002c-G_002e-29">Bolch, G.</a>: <a href="#Expected-Time-to-Absorption">Expected Time to Absorption</a></li>
+<li><a href="#index-Buzen_002c-J_002e-P_002e-101">Buzen, J. P.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Casale_002c-G_002e-256">Casale, G.</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-Casale_002c-G_002e-159">Casale, G.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-94">de Meer, H.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-69">de Meer, H.</a>: <a href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-63">de Meer, H.</a>: <a href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-55">de Meer, H.</a>: <a href="#The-M_002fM_002finf-System">The M/M/inf System</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-48">de Meer, H.</a>: <a href="#The-M_002fM_002fm-System">The M/M/m System</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-41">de Meer, H.</a>: <a href="#The-M_002fM_002f1-System">The M/M/1 System</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-31">de Meer, H.</a>: <a href="#Expected-Time-to-Absorption">Expected Time to Absorption</a></li>
+<li><a href="#index-Graham_002c-G_002e-S_002e-232">Graham, G. S.</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-Graham_002c-G_002e-S_002e-131">Graham, G. S.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Greiner_002c-S_002e-93">Greiner, S.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Greiner_002c-S_002e-68">Greiner, S.</a>: <a href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a></li>
+<li><a href="#index-Greiner_002c-S_002e-62">Greiner, S.</a>: <a href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a></li>
+<li><a href="#index-Greiner_002c-S_002e-54">Greiner, S.</a>: <a href="#The-M_002fM_002finf-System">The M/M/inf System</a></li>
+<li><a href="#index-Greiner_002c-S_002e-47">Greiner, S.</a>: <a href="#The-M_002fM_002fm-System">The M/M/m System</a></li>
+<li><a href="#index-Greiner_002c-S_002e-40">Greiner, S.</a>: <a href="#The-M_002fM_002f1-System">The M/M/1 System</a></li>
+<li><a href="#index-Greiner_002c-S_002e-30">Greiner, S.</a>: <a href="#Expected-Time-to-Absorption">Expected Time to Absorption</a></li>
+<li><a href="#index-Hsieh_002c-C_002e-H-254">Hsieh, C. H</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-Jain_002c-R_002e-141">Jain, R.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Kobayashi_002c-H_002e-113">Kobayashi, H.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Lam_002c-S_002e-255">Lam, S.</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-Lavenberg_002c-S_002e-S_002e-140">Lavenberg, S. S.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Lazowska_002c-E_002e-D_002e-230">Lazowska, E. D.</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-Lazowska_002c-E_002e-D_002e-129">Lazowska, E. D.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Muntz_002c-R_002e-R_002e-257">Muntz, R. R.</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-Reiser_002c-M_002e-112">Reiser, M.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Santini_002c-S_002e-275">Santini, S.</a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-Schweitzer_002c-P_002e-201">Schweitzer, P.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Schwetman_002c-H_002e-274">Schwetman, H.</a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-Schwetman_002c-H_002e-111">Schwetman, H.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Serazzi_002c-G_002e-258">Serazzi, G.</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-Sevcik_002c-K_002e-C_002e-233">Sevcik, K. C.</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-Sevcik_002c-K_002e-C_002e-132">Sevcik, K. C.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-95">Trivedi, K.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-70">Trivedi, K.</a>: <a href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-64">Trivedi, K.</a>: <a href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-56">Trivedi, K.</a>: <a href="#The-M_002fM_002finf-System">The M/M/inf System</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-49">Trivedi, K.</a>: <a href="#The-M_002fM_002fm-System">The M/M/m System</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-42">Trivedi, K.</a>: <a href="#The-M_002fM_002f1-System">The M/M/1 System</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-32">Trivedi, K.</a>: <a href="#Expected-Time-to-Absorption">Expected Time to Absorption</a></li>
+<li><a href="#index-Wong_002c-E_002e-280">Wong, E.</a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-Zahorjan_002c-J_002e-279">Zahorjan, J.</a>: <a href="#Utility-functions">Utility functions</a></li>
+<li><a href="#index-Zahorjan_002c-J_002e-231">Zahorjan, J.</a>: <a href="#Bounds-on-performance">Bounds on performance</a></li>
+<li><a href="#index-Zahorjan_002c-J_002e-130">Zahorjan, J.</a>: <a href="#Algorithms-for-Product_002dForm-QNs">Algorithms for Product-Form QNs</a></li>
+   </ul></body></html>
+
Binary file main/queueing/doc/queueing.pdf has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/queueing.texi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,203 @@
+% Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+%
+% This file is part of the queueing toolbox, a Queueing Networks analysis
+% package for GNU Octave
+%
+% The queueing toolbox 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 3 of the License, or (at
+% your option) any later version.
+% 
+% The queueing toolbox 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.
+% 
+% You should have received a copy of the GNU General Public License
+% along with the queueing toolbox; see the file COPYING.  If not, see
+% <http://www.gnu.org/licenses/>.
+
+% This info file is take from the GNU Octave info file
+
+\input texinfo
+@setfilename queueing.info
+
+@c The following macro is used for the on-line help system, but we don't
+@c want lots of `See also: foo, bar, and baz' strings cluttering the
+@c printed manual (that information should be in the supporting text for
+@c each group of functions and variables).
+
+@macro seealso {args}
+@iftex
+@vskip 2pt
+@end iftex
+@ifnottex
+@sp 1
+@end ifnottex
+@noindent
+@strong{See also:} \args\.
+@end macro
+
+@macro examplefile{file}
+@example
+@group
+@verbatiminclude @value{top_srcdir}/examples/\file\
+@end group
+@end example
+@end macro
+
+@ifinfo
+@format
+START-INFO-DIR-ENTRY
+* queueing: (octave).	Queueing Networks and Markov chains analysis package.
+END-INFO-DIR-ENTRY
+@end format
+@end ifinfo
+
+@c Settings for printing on 8-1/2 by 11 inch paper:
+@c -----------------------------------------------
+
+@setchapternewpage odd
+
+@c Settings for small book format:
+@c ------------------------------
+
+@ignore
+@smallbook
+@setchapternewpage odd
+@finalout
+@iftex
+@cropmarks
+@end iftex
+@end ignore
+
+@defindex op
+@defindex au
+
+@c Things like the Octave version number are defined in conf.texi.
+@c This file doesn't include a chapter, so it must not be included
+@c if you want to run the Emacs function texinfo-multiple-files-update.
+
+@include conf.texi
+
+@settitle queueing
+@documentdescription
+User manual for the queueing toolbox, a GNU Octave package for queueing networks and Markov chains analysis. This package supports single-station queueing systems, queueing networks and Markov chains. The queueing toolbox implements, among others, the Mean Value Analysis (MVA) and convolution algorithms for product-form queueing networks. Transient and steady-state analysis of Markov chains is also implemented.
+@end documentdescription
+
+@ifnottex
+
+Copyright @copyright{} 2008, 2009, 2010, 2011, 2012 Moreno Marzolla.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through Tex and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided that
+the entire resulting derived work is distributed under the terms of
+a permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for
+modified versions.
+@end ifnottex
+
+@titlepage
+@title The Octave Queueing Toolbox
+@subtitle User's Guide, Edition 1 for release @value{VERSION}
+@subtitle @value{VERSIONDATE}
+@author Moreno Marzolla
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 2008, 2009, 2010, 2011, 2012 Moreno Marzolla (@email{marzolla@@cs.unibo.it}).
+
+This is the first edition of the Queueing Toolbox documentation, and
+is consistent with version @value{VERSION} of the package.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the same conditions as for modified versions.
+
+Portions of this document have been adapted from the @code{octave}
+manual, Copyright @copyright{} John W. Eaton.
+@end titlepage
+
+@contents
+
+@ifnottex
+@node Top
+@top
+
+This manual documents how to install and run the Queueing Toolbox.
+It corresponds to version @value{VERSION} of the package.
+@end ifnottex
+
+@c ------------------------------------------------------------------------
+
+@menu
+* Summary::
+* Installation::                Installation of the queueing toolbox.
+* Getting Started::             Getting started with the queueing toolbox.
+* Markov Chains::               Functions for Markov Chains.
+* Single Station Queueing Systems:: Functions for single-station queueing systems.
+* Queueing Networks::           Functions for queueing networks.
+* Contributing Guidelines::     How to contribute.
+* Acknowledgements::            People who contributed to the queueing toolbox.
+* Copying::                     The GNU General Public License.
+* Concept Index::               An item for each concept.
+* Function Index::              An item for each function.
+* Author Index::                An item for each author.
+@end menu
+
+@c ------------------------------------------------------------------------
+
+@include summary.texi
+@include installation.texi
+@include gettingstarted.texi
+@include markovchains.texi
+@include singlestation.texi
+@include queueingnetworks.texi
+
+@c
+@c Appendix starts here
+@c
+@include contributing.texi
+@include ack.texi
+@include gpl.texi
+
+@c
+@c INDEX
+@c
+
+@node Concept Index
+@unnumbered Concept Index
+
+@printindex cp
+
+@node Function Index
+@unnumbered Function Index
+
+@printindex fn
+
+@node Author Index
+@unnumbered Author Index
+
+@printindex au
+
+@bye
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/queueingnetworks.txi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,1196 @@
+@c -*- texinfo -*-
+
+@c Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+@c
+@c This file is part of the queueing toolbox, a Queueing Networks
+@c analysis package for GNU Octave.
+@c
+@c The queueing toolbox is free software; you can redistribute it
+@c and/or modify it under the terms of the GNU General Public License
+@c as published by the Free Software Foundation; either version 3 of
+@c the License, or (at your option) any later version.
+@c
+@c The queueing toolbox is distributed in the hope that it will be
+@c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+@c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+@c GNU General Public License for more details.
+@c
+@c You should have received a copy of the GNU General Public License
+@c along with the queueing toolbox; see the file COPYING.  If not, see
+@c <http://www.gnu.org/licenses/>.
+
+@node Queueing Networks
+@chapter Queueing Networks
+
+@menu
+* Introduction to QNs::                 A brief introduction to Queueing Networks.
+* Generic Algorithms::                  High-level functions for QN analysis
+* Algorithms for Product-Form QNs::     Functions to analyze product-form QNs
+* Algorithms for non Product-form QNs:: Functions to analyze non product-form QNs
+* Bounds on performance::               Functions to compute performance bounds
+* Utility functions::                   Utility functions to compute miscellaneous quantities
+@end menu
+
+@cindex queueing networks
+
+@c
+@c INTRODUCTION
+@c 
+@node Introduction to QNs
+@section Introduction to QNs
+
+Queueing Networks (QN) are a very simple yet powerful modeling tool
+which is used to analyze many kind of systems. In its simplest form, a
+QN is made of @math{K} service centers. Each service center @math{i}
+has a queue, which is connected to @math{m_i} (generally identical)
+@emph{servers}. Customers (or requests) arrive at the service center,
+and join the queue if there is a slot available. Then, requests are
+served according to a (de)queueing policy. After service completes,
+the requests leave the service center.
+
+The service centers for which @math{m_i = \infty} are called
+@emph{delay centers} or @emph{infinite servers}. If a service center
+has infinite servers, of course each new request will find one server
+available, so there will never be queueing.
+
+Requests join the queue according to a @emph{queueing policy}, such as:
+
+@table @strong
+
+@item FCFS
+First-Come-First-Served
+
+@item LCFS-PR
+Last-Come-First-Served, Preemptive Resume
+
+@item PS
+Processor Sharing
+
+@item IS
+Infinite Server, there is an infinite number of identical servers so
+that each request always finds a server available, and there is no
+queueing
+
+@end table
+
+A population of @emph{requests} or @emph{customers} arrives to the
+system system, requesting service to the service centers.  The request
+population may be @emph{open} or @emph{closed}. In open systems there
+is an infinite population of requests. New customers arrive from
+outside the system, and eventually leave the system. In closed systems
+there is a fixed population of request which continuously interacts
+with the system.
+
+There might be a single class of requests, meaning that all requests
+behave in the same way (e.g., they spend the same average time on each
+particular server), or there might be multiple classes of requests.
+
+@subsection Single class models
+
+In single class models, all requests are indistinguishable and belong to
+the same class. This means that every request has the same average
+service time, and all requests move through the system with the same
+routing probabilities.
+
+@noindent @strong{Model Inputs}
+
+@table @math
+
+@item \lambda_i
+External arrival rate to service center @math{i}.
+
+@item \lambda
+Overall external arrival rate to the whole system: @math{\lambda =
+\sum_i \lambda_i}.
+
+@item S_i
+Average service time. @math{S_i} is the average service time on service
+center @math{i}. In other words, @math{S_i} is the average time from the
+instant in which a request is extracted from the queue and starts being
+service, and the instant at which service finishes and the request moves
+to another queue (or exits the system).
+
+@item P_{ij}
+Routing probability matrix. @math{{\bf P} = P_{ij}} is a @math{K \times
+K} matrix such that @math{P_{ij}} is the probability that a request
+completing service at server @math{i} will move directly to server
+@math{j}, The probability that a request leaves the system after service
+at service center @math{i} is @math{1-\sum_{j=1}^K P_{ij}}.
+
+@item V_i
+Average number of visits. @math{V_i} is the average number of visits to
+the service center @math{i}. This quantity will be described shortly.
+
+@end table
+
+@noindent @strong{Model Outputs}
+
+@table @math
+
+@item U_i
+Service center utilization. @math{U_i} is the utilization of service
+center @math{i}. The utilization is defined as the fraction of time in
+which the resource is busy (i.e., the server is processing requests).
+
+@item R_i
+Average response time. @math{R_i} is the average response time of
+service center @math{i}. The average response time is defined as the
+average time between the arrival of a customer in the queue, and the
+completion of service.
+
+@item Q_i
+Average number of customers. @math{Q_i} is the average number of
+requests in service center @math{i}. This includes both the requests in
+the queue, and the request being served.
+
+@item X_i
+Throughput. @math{X_i} is the throughput of service center @math{i}.
+The throughput is defined as the ratio of job completions (i.e., average
+number of jobs completed over a fixed interval of time).
+
+@end table
+
+@noindent Given these output parameters, additional performance measures can
+be computed as follows:
+
+@table @math
+
+@item X
+System throughput, @math{X = X_1 / V_1}
+
+@item R
+System response time, @math{R = \sum_{k=1}^K R_k V_k}
+
+@item Q
+Average number of requests in the system, @math{Q = N-XZ}
+
+@end table
+
+For open, single-class models, the scalar @math{\lambda} denotes the
+external arrival rate of requests to the system. The average number of
+visits satisfy the following equation:
+
+@iftex
+@tex
+$V_j = P_{0 j} + \sum_{i=1}^K V_i P_{i j}$
+@end tex
+@end iftex
+@ifnottex
+@example
+V == P0 + V*P;
+@end example
+@end ifnottex
+
+@noindent where @math{P_{0 j}} is the probability that an external 
+arrival goes to service center @math{j}. If @math{\lambda_j} is the
+external arrival rate to service center @math{j}, and @math{\lambda =
+\sum_j \lambda_j} is the overall external arrival rate, then
+@math{P_{0 j} = \lambda_j / \lambda}.
+
+For closed models, the visit ratios satisfy the following equation:
+
+@iftex
+@tex
+$V_j = \sum_{i=1}^K V_i P_{i j}$
+@end tex
+@end iftex
+@ifnottex
+@example
+V(1) == 1 && V == V*P;
+@end example
+@end ifnottex
+
+@subsection Multiple class models
+
+In multiple class QN models, we assume that there exist @math{C}
+different classes of requests. Each request from class @math{c} spends
+on average time @math{S_{ck}} in service at service center @math{k}. For
+open models, we denote with @math{{\bf \lambda} = \lambda_{ck}} the
+arrival rates, where @math{\lambda_{ck}} is the external arrival rate of
+class @math{c} customers at service center @math{k}. For closed models,
+we denote with @math{{\bf N} = (N_1, N_2, \ldots N_C)} the population
+vector, where @math{N_c} is the number of class @math{c} requests in the
+system.
+
+The transition probability matrix for these kind of networks will be a
+@math{C \times K \times C \times K} matrix @math{{\bf P} =
+P_{risj}} such that @math{P_{risj}} is the probability that a
+class @math{r} request which completes service at center @math{i} will
+join server @math{j} as a class @math{s} request.
+
+Model input and outputs can be adjusted by adding additional
+indexes for the customer classes.
+
+@noindent @strong{Model Inputs}
+
+@table @math
+
+@item \lambda_{ci}
+External arrival rate of class-@math{c} requests to service center @math{i}
+
+@item \lambda
+Overall external arrival rate to the whole system: @math{\lambda = \sum_c \sum_i \lambda_{ci}}
+
+@item S_{ci}
+Average service time. @math{S_{ci}} is the average service time on service
+center @math{i} for class @math{c} requests.
+
+@item P_{risj}
+Routing probability matrix. @math{{\bf P} = P_{risj}} is a @math{C
+\times K \times C \times K} matrix such that @math{P_{risj}} is the
+probability that a class @math{r} request which completes service at
+server @math{i} will move to server @math{j} as a class @math{s}
+request.
+
+@item V_{ci}
+Average number of visits. @math{V_{ci}} is the average number of visits
+of class @math{c} requests to the service center @math{i}.
+
+@end table
+
+@noindent @strong{Model Outputs}
+
+@table @math
+
+@item U_{ci}
+Utilization of service center @math{i} by class @math{c} requests. The
+utilization is defined as the fraction of time in which the resource is
+busy (i.e., the server is processing requests).
+
+@item R_{ci}
+Average response time experienced by class @math{c} requests on service
+center @math{i}. The average response time is defined as the average
+time between the arrival of a customer in the queue, and the completion
+of service.
+
+@item Q_{ci}
+Average number of class @math{c} requests on service center
+@math{i}. This includes both the requests in the queue, and the request
+being served.
+
+@item X_{ci}
+Throughput of service center @math{i} for class @math{c} requests.  The
+throughput is defined as the rate of completion of class @math{c}
+requests.
+
+@end table
+
+@noindent It is possible to define aggregate performance measures as follows:
+
+@table @math
+
+@item U_i
+Utilization of service center @math{i}:
+@iftex
+@tex
+$U_i = \sum_{c=1}^C U_{ci}$
+@end tex
+@end iftex
+@ifnottex
+@code{Ui = sum(U,1);}
+@end ifnottex
+
+@item R_c
+System response time for class @math{c} requests:
+@iftex
+@tex
+$R_c = \sum_{i=1}^K R_{ci} V_{ci}$
+@end tex
+@end iftex
+@ifnottex
+@code{Rc = sum( V.*R, 1 );}
+@end ifnottex
+
+@item Q_c
+Average number of class @math{c} requests in the system:
+@iftex
+@tex
+$Q_c = \sum_{i=1}^K Q_{ci}$
+@end tex
+@end iftex
+@ifnottex
+@code{Qc = sum( Q, 2 );}
+@end ifnottex
+
+@item X_c
+Class @math{c} throughput:
+@iftex
+@tex
+$X_c = X_{c1} / V_{c1}$
+@end tex
+@end iftex
+@ifnottex
+@code{Xc = X(:,1) ./ V(:,1);}
+@end ifnottex
+
+@end table
+
+We can define the visit ratios @math{V_{sj}} for class @math{s}
+customers at service center @math{j} as follows:
+
+@iftex
+@tex
+$V_{sj} = \sum_{r=1}^C \sum_{i=1}^K V_{ri} P_{risj},\ V_{s1} = 1$
+@end tex
+@end iftex
+@ifnottex
+@group
+V_sj = sum_r sum_i V_ri P_risj, for all s,j
+@end group
+@end ifnottex
+
+@noindent while for open networks:
+
+@iftex
+@tex
+$V_{sj} = P_{0sj} + \sum_{r=1}^C \sum_{i=1}^K V_{ri} P_{risj}$
+@end tex
+@end iftex
+@ifnottex
+@group
+V_sj = P_0sj + sum_r sum_i V_ri P_risj, for all s,j
+@end group
+@end ifnottex
+
+@noindent where @math{P_{0sj}} is the probability that an external 
+arrival goes to service center @math{j} as a class-@math{s} request.
+If @math{\lambda_{sj}} is the external arrival rate of class @math{s}
+requests to service center @math{j}, and @math{\lambda = \sum_s \sum_j
+\lambda_{sj}} is the overall external arrival rate to the whole system,
+then @math{P_{0sj} = \lambda_{sj} / \lambda}.
+
+@c
+@c
+@c
+@node Generic Algorithms
+@section Generic Algorithms
+
+The @code{queueing} package provides a couple of high-level functions
+for defining and solving QN models. These functions can be used to
+define a open or closed QN model (with single or multiple job
+classes), with arbitrary configuration and queueing disciplines. At
+the moment only product-form networks can be solved, @xref{Algorithms for Product-Form QNs}.
+
+The network is defined by two parameters. The first one is the list of
+nodes, encoded as an Octave @emph{cell array}. The second parameter is
+the visit ration @var{V}, which can be either a vector (for
+single-class models) or a two-dimensional matrix (for multiple-class
+models).
+
+Individual nodes in the network are structures build using the
+@code{qnmknode} function.
+
+@DOCSTRING(qnmknode)
+
+After the network has been defined, it is possible to solve it using
+the @code{qnsolve} function. Note that this function is somewhat less
+efficient than those described in later sections, but
+generally easier to use.
+
+@DOCSTRING(qnsolve)
+
+@noindent @strong{EXAMPLE}
+
+Let us consider a closed, multiclass network with @math{C=2} classes
+and @math{K=3} service center. Let the population be @math{M=(2, 1)}
+(class 1 has 2 requests, and class 2 has 1 request). The nodes are as
+follows:
+
+@itemize
+
+@item Node 1 is a @math{M/M/1}--FCFS node, with load-dependent service
+times. Service times are class-independent, and are defined by the
+matrix @code{[0.2 0.1 0.1; 0.2 0.1 0.1]}. Thus, @code{@var{S}(1,2) =
+0.2} means that service time for class 1 customers where there are 2
+requests in 0.2. Note that service times are class-independent;
+
+@item Node 2 is a @math{-/G/1}--PS node, with service times 
+@math{S_{12} = 0.4} for class 1, and @math{S_{22} = 0.6} for class 2
+requests;
+
+@item Node 3 is a @math{-/G/\infty} node (delay center), with service
+times @math{S_{13}=1} and @math{S_{23}=2} for class 1 and 2
+respectively.
+
+@end itemize
+
+After defining the per-class visit count @var{V} such that
+@code{@var{V}(c,k)} is the visit count of class @math{c} requests to
+service center @math{k}.  We can define and solve the model as
+follows:
+
+@example
+@verbatiminclude @value{top_srcdir}/examples/demo_1_qnsolve.m
+@end example
+
+
+@c
+@c
+@c
+@node Algorithms for Product-Form QNs
+@section Algorithms for Product-Form QNs
+
+Product-form queueing networks fulfill the following assumptions:
+
+@itemize
+
+@item The network can consist of open and closed job classes.
+
+@item The following queueing disciplines are allowed: FCFS, PS, LCFS-PR and IS.
+
+@item Service times for FCFS nodes must be exponentially distributed and
+class-independent. Service centers at PS, LCFS-PR and IS nodes can
+have any kind of service time distribution with a rational Laplace
+transform.  Furthermore, for PS, LCFS-PR and IS nodes, different
+classes of customers can have different service times.
+
+@item The service rate of an FCFS node is only allowed to depend on the
+number of jobs at this node; in a PS, LCFS-PR and IS node the service
+rate for a particular job class can also depend on the number of jobs
+of that class at the node.
+
+@item In open networks two kinds of arrival processes are allowed: i) the
+arrival process is Poisson, with arrival rate @math{\lambda} which can
+depend on the number of jobs in the network. ii) the arrival process
+consists of @math{U} independent Poisson arrival streams where the
+@math{U} job sources are assigned to the @math{U} chains; the arrival
+rate can be load dependent.
+
+@end itemize
+
+@c
+@c Jackson Networks
+@c
+
+@subsection Jackson Networks
+
+Jackson networks satisfy the following conditions:
+
+@itemize
+
+@item 
+There is only one job class in the network; the overall number of jobs
+in the system is unlimited.
+
+@item 
+There are @math{N} service centers in the network. Each service center
+may have Poisson arrivals from outside the system. A job can leave
+the system from any node.
+
+@item 
+Arrival rates as well as routing probabilities are independent from
+the number of nodes in the network.
+
+@item 
+External arrivals and service times at the service centers are
+exponentially distributed, and in general can be load-dependent.
+
+@item
+Service discipline at each node is FCFS
+
+@end itemize
+
+We define the @emph{joint probability vector} @math{\pi(k_1, k_2,
+\ldots k_N)} as the steady-state probability that there are @math{k_i}
+requests at service center @math{i}, for all @math{i=1,2, \ldots N}.
+Jackson networks have the property that the joint probability is the
+product of the marginal probabilities @math{\pi_i}:
+
+@iftex
+@tex
+$$ \pi(k_1, k_2, \ldots k_N) = \prod_{i=1}^N \pi_i(k_i) $$
+@end tex
+@end iftex
+@ifnottex
+@example
+@var{joint_prob} = prod( @var{pi} )
+@end example
+@end ifnottex
+
+@noindent where @math{\pi_i(k_i)} is the steady-state probability
+that there are @math{k_i} requests at service center @math{i}.
+
+@DOCSTRING(qnjackson)
+
+@noindent @strong{REFERENCES}
+
+This implementation is based on G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, @cite{Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications}, Wiley,
+1998, pp. 284--287.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@subsection The Convolution Algorithm
+
+According to the BCMP theorem, the state probability of a closed
+single class queueing network with @math{K} nodes and @math{N} requests
+can be expressed as:
+
+@iftex
+@tex
+$$ \pi(k_1, k_2, \ldots k_K) = {1 \over G(N)} \prod_{i=1}^N F_i(k_i) $$
+@end tex
+@end iftex
+@ifnottex
+@example
+@group
+k = [k1, k2, @dots{} kn]; @r{population vector}
+p = 1/G(N+1) \prod F(i,k);
+@end group
+@end example
+@end ifnottex
+
+Here @math{\pi(k_1, k_2, \ldots k_K)} is the joint probability of
+having @math{k_i} requests at node @math{i}, for all @math{i=1,2,
+\ldots K}.
+
+The @emph{convolution algorithms} computes the normalization constants
+@math{G = (G(0), G(1), \ldots G(N))} for single-class, closed networks
+with @math{N} requests.  The normalization constants are returned as
+vector @code{@var{G}=[@var{G}(1), @var{G}(2), ... @var{G}(N+1)]} where
+@code{@var{G}(i+1)} is the value of @math{G(i)} (remember that Octave
+uses 1-base vectors). The normalization constant can be used to
+compute all performance measures of interest (utilization, average
+response time and so on).
+
+@code{queueing} implements the convolution algorithm, in the function
+@code{qnconvolution} and @code{qnconvolutionld}. The first one
+supports single-station nodes, multiple-station nodes and IS nodes.
+The second one supports networks with general load-dependent service
+centers.
+
+@c
+@c The Convolution Algorithm
+@c
+
+@DOCSTRING(qnconvolution)
+
+@noindent @strong{EXAMPLE}
+
+The normalization constant @math{G} can be used to compute the
+steady-state probabilities for a closed single class product-form
+Queueing Network with @math{K} nodes. Let @code{@var{k}=[@math{k_1,
+k_2, @dots{} k_K}]} be a valid population vector. Then, the
+steady-state probability @code{@var{p}(i)} to have @code{@var{k}(i)}
+requests at service center @math{i} can be computed as:
+
+@iftex
+@tex
+$$
+p_i(k_i) = {(V_i S_i)^{k_i} \over G(K)} \left(G(K-k_i) - V_i S_i G(K-k_i-1)\right), \quad i=1,2, \ldots K
+$$
+@end tex
+@end iftex
+
+@example
+@verbatiminclude @value{top_srcdir}/examples/demo_1_qnconvolution.m
+@print{} k(1)=1 prob=0.17975
+@print{} k(2)=2 prob=0.48404
+@print{} k(3)=0 prob=0.52779
+@end example
+
+@noindent @strong{NOTE}
+
+For a network with @math{K} service centers and @math{N} requests,
+this implementation of the convolution algorithm has time and space
+complexity @math{O(NK)}.
+
+@noindent @strong{REFERENCES}
+
+Jeffrey P. Buzen, @cite{Computational Algorithms for Closed Queueing
+Networks with Exponential Servers}, Communications of the ACM, volume
+16, number 9, september 1973,
+pp. 527--531. @url{http://doi.acm.org/10.1145/362342.362345}
+
+@auindex Buzen, J. P.
+
+This implementation is based on G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, @cite{Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications}, Wiley,
+1998, pp. 313--317.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@c
+@c Convolution for load-dependent service centers
+@c
+@DOCSTRING(qnconvolutionld)
+
+@noindent @strong{REFERENCES}
+
+Herb Schwetman, @cite{Some Computational Aspects of Queueing Network
+Models}, Technical Report CSD-TR-354, Department of Computer Sciences,
+Purdue University, feb, 1981 (revised).
+@url{http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-354.pdf}
+
+@auindex Schwetman, H.
+
+M. Reiser, H. Kobayashi, @cite{On The Convolution Algorithm for
+Separable Queueing Networks}, In Proceedings of the 1976 ACM
+SIGMETRICS Conference on Computer Performance Modeling Measurement and
+Evaluation (Cambridge, Massachusetts, United States, March 29--31,
+1976). SIGMETRICS '76. ACM, New York, NY,
+pp. 109--117. @url{http://doi.acm.org/10.1145/800200.806187}
+
+@auindex Reiser, M.
+@auindex Kobayashi, H.
+
+This implementation is based on G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, @cite{Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications}, Wiley,
+1998, pp. 313--317. Function @code{qnconvolutionld} is slightly
+different from the version described in Bolch et al. because it
+supports general load-dependent centers (while the version in the book
+does not). The modification is in the definition of function
+@code{F()} in @code{qnconvolutionld} which has been made similar to
+function @math{f_i} defined in Schwetman, @code{Some Computational
+Aspects of Queueing Network Models}.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+
+@subsection Open networks
+
+@c
+@c Open networks with single class
+@c
+@DOCSTRING(qnopensingle)
+
+From the results computed by this function, it is possible to derive
+other quantities of interest as follows:
+
+@itemize
+
+@item
+@strong{System Response Time}: The overall system response time
+can be computed as
+@iftex
+@tex 
+$R_s = \sum_{i=1}^K V_i R_i$
+@end tex
+@end iftex
+@ifnottex
+@code{R_s = dot(V,R);}
+@end ifnottex
+
+@item
+@strong{Average number of requests}: The average number of requests
+in the system can be computed as:
+@iftex
+@tex 
+$Q_s = \sum_{i=1}^K Q(i)$
+@end tex
+@end iftex
+@ifnottex
+@code{Q_s = sum(Q)}
+@end ifnottex
+
+@end itemize
+
+@noindent @strong{EXAMPLE}
+
+@example
+@verbatiminclude @value{top_srcdir}/examples/demo_1_qnopensingle.m
+@print{} R_s =  1.4062
+@print{} N =  4.2186
+@end example
+
+@noindent @strong{REFERENCES}
+
+G. Bolch, S. Greiner, H. de Meer and K. Trivedi, @cite{Queueing
+Networks and Markov Chains: Modeling and Performance Evaluation with
+Computer Science Applications}, Wiley, 1998.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+
+@c
+@c Open network with multiple classes
+@c
+@DOCSTRING(qnopenmulti)
+
+@noindent @strong{REFERENCES}
+
+Edward D. Lazowska, John Zahorjan, G. Scott Graham, and Kenneth C.
+Sevcik, @cite{Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models}, Prentice Hall,
+1984. @url{http://www.cs.washington.edu/homes/lazowska/qsp/}. In
+particular, see section 7.4.1 ("Open Model Solution Techniques").
+
+@auindex Lazowska, E. D. 
+@auindex Zahorjan, J. 
+@auindex Graham, G. S.
+@auindex Sevcik, K. C.
+
+
+@subsection Closed Networks
+
+@c
+@c MVA for single class, closed networks
+@c
+
+@DOCSTRING(qnclosedsinglemva)
+
+From the results provided by this function, it is possible to derive 
+other quantities of interest as follows:
+
+@noindent @strong{EXAMPLE}
+
+@example
+@verbatiminclude @value{top_srcdir}/examples/demo_1_qnclosedsinglemva.m
+@end example
+
+
+@noindent @strong{REFERENCES}
+
+M. Reiser and S. S. Lavenberg, @cite{Mean-Value Analysis of Closed
+Multichain Queuing Networks}, Journal of the ACM, vol. 27, n. 2, April
+1980, pp. 313--322. @url{http://doi.acm.org/10.1145/322186.322195}
+
+@auindex Reiser, M.
+@auindex Lavenberg, S. S.
+
+This implementation is described in R. Jain , @cite{The Art of Computer
+Systems Performance Analysis}, Wiley, 1991, p. 577.  Multi-server nodes
+@c and the computation of @math{G(N)}, 
+are treated according to G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, @cite{Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications}, Wiley,
+1998, Section 8.2.1, "Single Class Queueing Networks".
+
+@auindex Jain, R.
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@c
+@c MVA for single class, closed networks with load dependent servers
+@c
+@DOCSTRING(qnclosedsinglemvald)
+
+@noindent @strong{REFERENCES}
+
+M. Reiser and S. S. Lavenberg, @cite{Mean-Value Analysis of Closed
+Multichain Queuing Networks}, Journal of the ACM, vol. 27, n. 2,
+April 1980, pp. 313--322. @url{http://doi.acm.org/10.1145/322186.322195}
+
+This implementation is described in G. Bolch, S. Greiner, H. de Meer
+and K. Trivedi, @cite{Queueing Networks and Markov Chains: Modeling
+and Performance Evaluation with Computer Science Applications}, Wiley,
+1998, Section 8.2.4.1, ``Networks with Load-Deèpendent Service: Closed
+Networks''.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@c
+@c CMVA for single class, closed networks with a single load dependent servers
+@c
+@DOCSTRING(qncmva)
+
+@noindent @strong{REFERENCES}
+
+G. Casale. @cite{A note on stable flow-equivalent aggregation in
+closed networks}. Queueing Syst. Theory Appl., 60:193–202, December
+2008.
+
+@auindex Casale, G.
+
+@c
+@c Approximate MVA for single class, closed networks
+@c
+
+@DOCSTRING(qnclosedsinglemvaapprox)
+
+@noindent @strong{REFERENCES}
+
+This implementation is based on Edward D. Lazowska, John Zahorjan,
+G. Scott Graham, and Kenneth C. Sevcik, @cite{Quantitative System
+Performance: Computer System Analysis Using Queueing Network Models},
+Prentice Hall,
+1984. @url{http://www.cs.washington.edu/homes/lazowska/qsp/}. In
+particular, see section 6.4.2.2 ("Approximate Solution Techniques").
+
+@auindex Lazowska, E. D.
+@auindex Zahorjan, J.
+@auindex Graham, G. S.
+@auindex Sevcik, K. C.
+
+
+@c
+@c MVA for multiple class, closed networks
+@c
+@DOCSTRING(qnclosedmultimva)
+
+@noindent @strong{NOTE}
+
+Given a network with @math{K} service centers, @math{C} job classes and
+population vector @math{{\bf N}=(N_1, N_2, \ldots N_C)}, the MVA
+algorithm requires space @math{O(C \prod_i (N_i + 1))}. The time
+complexity is @math{O(CK\prod_i (N_i + 1))}. This implementation is
+slightly more space-efficient (see details in the code). While the space
+requirement can be mitigated by using some optimizations, the time
+complexity can not. If you need to analyze large closed networks you
+should consider the @command{qnclosedmultimvaapprox} function, which
+implements the approximate MVA algorithm. Note however that
+@command{qnclosedmultimvaapprox} will only provide approximate results.
+
+
+@noindent @strong{REFERENCES}
+
+M. Reiser and S. S. Lavenberg, @cite{Mean-Value Analysis of Closed
+Multichain Queuing Networks}, Journal of the ACM, vol. 27, n. 2, April
+1980, pp. 313--322. @url{http://doi.acm.org/10.1145/322186.322195}
+
+@auindex Reiser, M.
+@auindex Lavenberg, S. S.
+
+This implementation is based on G. Bolch, S. Greiner, H. de Meer and
+K. Trivedi, @cite{Queueing Networks and Markov Chains: Modeling and
+Performance Evaluation with Computer Science Applications}, Wiley,
+1998 and Edward D. Lazowska, John Zahorjan, G. Scott Graham, and
+Kenneth C. Sevcik, @cite{Quantitative System Performance: Computer
+System Analysis Using Queueing Network Models}, Prentice Hall,
+1984. @url{http://www.cs.washington.edu/homes/lazowska/qsp/}. In
+particular, see section 7.4.2.1 ("Exact Solution Techniques").
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+@auindex Lazowska, E. D.
+@auindex Zahorjan, J.
+@auindex Graham, G. S.
+@auindex Sevcik, K. C.
+
+@c
+@c Approximate MVA, with Bard-Schweitzer approximation
+@c
+@DOCSTRING(qnclosedmultimvaapprox)
+
+@noindent @strong{REFERENCES}
+
+Y. Bard, @cite{Some Extensions to Multiclass Queueing Network Analysis},
+proc. 4th Int. Symp. on Modelling and Performance Evaluation of
+Computer Systems, feb. 1979, pp. 51--62.
+
+@auindex Bard, Y.
+
+P. Schweitzer, @cite{Approximate Analysis of Multiclass Closed
+Networks of Queues}, Proc. Int. Conf. on Stochastic Control and
+Optimization, jun 1979, pp. 25--29.
+
+@auindex Schweitzer, P.
+
+This implementation is based on Edward D. Lazowska, John Zahorjan, G.
+Scott Graham, and Kenneth C. Sevcik, @cite{Quantitative System
+Performance: Computer System Analysis Using Queueing Network Models},
+Prentice Hall,
+1984. @url{http://www.cs.washington.edu/homes/lazowska/qsp/}.  In
+particular, see section 7.4.2.2 ("Approximate Solution
+Techniques"). This implementation is slightly different from the one
+described above, as it computes the average response times @math{R}
+instead of the residence times.
+
+@auindex Lazowska, E. D.
+@auindex Zahorjan, J.
+@auindex Graham, G. S.
+@auindex Sevcik, K. C.
+
+@subsection Mixed Networks
+
+@c
+@c MVA for mixed networks
+@c
+@DOCSTRING(qnmix)
+
+@noindent @strong{REFERENCES}
+
+Edward D. Lazowska, John Zahorjan, G. Scott Graham, and Kenneth C.
+Sevcik, @cite{Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models}, Prentice Hall,
+1984. @url{http://www.cs.washington.edu/homes/lazowska/qsp/}. In
+particular, see section 7.4.3 ("Mixed Model Solution Techniques").
+Note that in this function we compute the mean response time @math{R}
+instead of the mean residence time as in the reference.
+
+@auindex Lazowska, E. D. 
+@auindex Zahorjan, J. 
+@auindex Graham, G. S.
+@auindex Sevcik, K. C.
+
+Herb Schwetman, @cite{Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models}, Technical Report CSD-TR-355,
+Department of Computer Sciences, Purdue University, feb 15, 1982,
+available at
+@url{http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-355.pdf}
+
+@auindex Schwetman, H.
+
+
+@node Algorithms for non Product-form QNs
+@section Algorithms for non Product-Form QNs
+
+@c
+@c MVABLO algorithm for approximate analysis of closed, single class
+@c QN with blocking
+@c
+@DOCSTRING(qnmvablo)
+
+@noindent @strong{REFERENCES}
+
+Ian F. Akyildiz, @cite{Mean Value Analysis for Blocking Queueing
+Networks}, IEEE Transactions on Software Engineering, vol. 14, n. 2,
+april 1988, pp. 418--428.  @url{http://dx.doi.org/10.1109/32.4663}
+
+@auindex Akyildiz, I. F.
+
+@DOCSTRING(qnmarkov)
+
+@c
+@c
+@c
+@node Bounds on performance
+@section Bounds on performance
+
+@c
+@DOCSTRING(qnopenab)
+
+@noindent @strong{REFERENCES}
+
+Edward D. Lazowska, John Zahorjan, G.  Scott Graham, and Kenneth
+C. Sevcik, @cite{Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models}, Prentice Hall,
+1984. @url{http://www.cs.washington.edu/homes/lazowska/qsp/}. In
+particular, see section 5.2 ("Asymptotic Bounds").
+
+@auindex Lazowska, E. D.
+@auindex Zahorjan, J.
+@auindex Graham, G. S.
+@auindex Sevcik, K. C.
+
+@c
+@DOCSTRING(qnclosedab)
+
+@noindent @strong{REFERENCES}
+
+@noindent Edward D. Lazowska, John Zahorjan, G.  Scott Graham, and Kenneth
+C. Sevcik, @cite{Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models}, Prentice Hall,
+1984. @url{http://www.cs.washington.edu/homes/lazowska/qsp/}. In
+particular, see section 5.2 ("Asymptotic Bounds").
+
+@auindex Lazowska, E. D.
+@auindex Zahorjan, J.
+@auindex Graham, G. S.
+@auindex Sevcik, K. C.
+
+
+@c
+@DOCSTRING(qnopenbsb)
+
+@noindent @strong{REFERENCES}
+
+Edward D. Lazowska, John Zahorjan, G.  Scott Graham, and Kenneth
+C. Sevcik, @cite{Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models}, Prentice Hall,
+1984. @url{http://www.cs.washington.edu/homes/lazowska/qsp/}. In
+particular, see section 5.4 ("Balanced Systems Bounds").
+
+@auindex Lazowska, E. D.
+@auindex Zahorjan, J.
+@auindex Graham, G. S.
+@auindex Sevcik, K. C.
+
+@c
+@DOCSTRING(qnclosedbsb)
+
+@c
+@DOCSTRING(qnclosedpb)
+
+@noindent @strong{REFERENCES}
+
+The original paper describing PB Bounds is C. H. Hsieh and S. Lam,
+@cite{Two classes of performance bounds for closed queueing networks},
+PEVA, vol. 7, n. 1, pp. 3--30, 1987
+
+This function implements the non-iterative variant described in G.
+Casale, R. R. Muntz, G. Serazzi, @cite{Geometric Bounds: a
+Non-Iterative Analysis Technique for Closed Queueing Networks}, IEEE
+Transactions on Computers, 57(6):780-794, June 2008.
+
+@auindex Hsieh, C. H 
+@auindex Lam, S.
+@auindex Casale, G.
+@auindex Muntz, R. R.
+@auindex Serazzi, G.
+
+@c
+@DOCSTRING(qnclosedgb)
+
+@noindent @strong{REFERENCES}
+
+G. Casale, R. R. Muntz, G. Serazzi,
+@cite{Geometric Bounds: a Non-Iterative Analysis Technique for Closed
+Queueing Networks}, IEEE Transactions on Computers, 57(6):780-794,
+June 2008. @url{http://doi.ieeecomputersociety.org/10.1109/TC.2008.37}
+
+@auindex Casale, G.
+@auindex Muntz, R. R.
+@auindex Serazzi, G.
+  
+In this implementation we set @math{X^+} and @math{X^-} as the upper
+and lower Asymptotic Bounds as computed by the @code{qnclosedab}
+function, respectively.
+
+@node Utility functions
+@section Utility functions
+
+@subsection Open or closed networks 
+
+@DOCSTRING(qnclosed)
+
+@noindent @strong{EXAMPLE}
+
+@example
+@verbatiminclude @value{top_srcdir}/examples/demo_1_qnclosed.m
+@end example
+
+@DOCSTRING(qnopen)
+
+@c
+@c Compute the visit counts
+@c
+
+@subsection Computation of the visit counts
+
+For single-class networks the average number of visits satisfy the
+following equation:
+
+@iftex
+@tex
+$V_j = P_{0 j} + \sum_{i=1}^K V_i P_{i j}$
+@end tex
+@end iftex
+@ifnottex
+@example
+V == P0 + V*P;
+@end example
+@end ifnottex
+
+@noindent where @math{P_{0 j}} is the probability that an external 
+arrival goes to service center @math{j}. If @math{\lambda_j} is the
+external arrival rate to service center @math{j}, and @math{\lambda =
+\sum_j \lambda_j} is the overall external arrival rate, then
+@math{P_{0 j} = \lambda_j / \lambda}. 
+
+For closed networks, the visit ratios satisfy the following equation:
+
+@iftex
+@tex
+$V_j = \sum_{i=1}^K V_i P_{i j},\ V_1 = 1$
+@end tex
+@end iftex
+@ifnottex
+@example
+V(1) == 1 && V == V*P;
+@end example
+@end ifnottex
+
+The definitions above can be extended to multiple class networks as
+follows. We define the visit ratios @math{V_{sj}} for class @math{s}
+customers at service center @math{j} as follows:
+
+@iftex
+@tex
+$V_{sj} = \sum_{r=1}^C \sum_{i=1}^K V_{ri} P_{risj},\ V_{s1} = 1$
+@end tex
+@end iftex
+@ifnottex
+@group
+V_sj = sum_r sum_i V_ri P_risj, for all s,j
+V_s1 = 1, for all s
+@end group
+@end ifnottex
+
+@noindent while for open networks:
+
+@iftex
+@tex
+$V_{sj} = P_{0sj} + \sum_{r=1}^C \sum_{i=1}^K V_{ri} P_{risj}$
+@end tex
+@end iftex
+@ifnottex
+@group
+V_sj = P_0sj + sum_r sum_i V_ri P_risj, for all s,j
+@end group
+@end ifnottex
+
+@noindent where @math{P_{0sj}} is the probability that an external 
+arrival goes to service center @math{j} as a class-@math{s} request.
+If @math{\lambda_{sj}} is the external arrival rate of class @math{s}
+requests to service center @math{j}, and @math{\lambda = \sum_s \sum_j
+\lambda_{sj}} is the overall external arrival rate to the whole system,
+then @math{P_{0sj} = \lambda_{sj} / \lambda}.
+
+@DOCSTRING(qnvisits)
+
+@noindent @strong{EXAMPLE}
+
+@example
+@verbatiminclude @value{top_srcdir}/examples/demo_1_qnvisits.m
+@end example
+
+@subsection Other utility functions
+
+@c
+@DOCSTRING(population_mix)
+
+@noindent @strong{REFERENCES}
+
+Herb Schwetman, @cite{Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models}, Technical Report CSD-TR-355,
+Department of Computer Sciences, Purdue University, feb 15, 1982,
+available at
+@url{http://www.cs.purdue.edu/research/technical_reports/1980/TR
+80-355.pdf}
+
+Note that the slightly different problem of generating all tuples
+@math{k_1, k_2, \ldots k_N} such that @math{\sum_i k_i = k} and
+@math{k_i} are nonnegative integers, for some fixed integer @math{k
+@geq{} 0} has been described in S. Santini, @cite{Computing the
+Indices for a Complex Summation}, unpublished report, available at
+@url{http://arantxa.ii.uam.es/~ssantini/writing/notes/s668_summation.pdf}
+
+@auindex Schwetman, H.
+@auindex Santini, S.
+
+@c
+@DOCSTRING(qnmvapop)
+
+@noindent @strong{REFERENCES}
+
+Zahorjan, J. and Wong, E. @cite{The solution of separable queueing
+network models using mean value analysis}. SIGMETRICS
+Perform. Eval. Rev. 10, 3 (Sep. 1981), 80-85. DOI
+@url{http://doi.acm.org/10.1145/1010629.805477}
+
+@auindex Zahorjan, J.
+@auindex Wong, E.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/singlestation.txi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,228 @@
+@c -*- texinfo -*-
+
+@c Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+@c
+@c This file is part of the queueing toolbox, a Queueing Networks
+@c analysis package for GNU Octave.
+@c
+@c The queueing toolbox is free software; you can redistribute it
+@c and/or modify it under the terms of the GNU General Public License
+@c as published by the Free Software Foundation; either version 3 of
+@c the License, or (at your option) any later version.
+@c
+@c The queueing toolbox is distributed in the hope that it will be
+@c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+@c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+@c GNU General Public License for more details.
+@c
+@c You should have received a copy of the GNU General Public License
+@c along with the queueing toolbox; see the file COPYING.  If not, see
+@c <http://www.gnu.org/licenses/>.
+
+@node Single Station Queueing Systems
+@chapter Single Station Queueing Systems
+
+Single Station Queueing Systems contain a single station, and are thus
+quite easy to analyze. The @code{queueing} package contains functions
+for handling the following types of queues:
+
+@ifnottex
+@menu
+* The M/M/1 System::    Single-server queueing station.
+* The M/M/m System::    Multiple-server queueing station.
+* The M/M/inf System::  Infinite-server (delay center) station.
+* The M/M/1/K System::  Single-server, finite-capacity queueing station.
+* The M/M/m/K System::  Multiple-server, finite-capacity queueing station.
+* The Asymmetric M/M/m System::  Asymmetric multiple-server queueing station.
+* The M/G/1 System:: Single-server with general service time distribution.
+* The M/Hm/1 System:: Single-server with hyperexponential service time distribution.
+@end menu
+@end ifnottex
+@iftex
+@itemize
+
+@item @math{M/M/1} single-server queueing station;
+
+@item @math{M/M/m} multiple-server queueing station;
+
+@item Asymmetric @math{M/M/m};
+
+@item @math{M/M/\infty} infinite-server station (delay center);
+
+@item @math{M/M/1/K} single-server, finite-capacity queueing station;
+
+@item @math{M/M/m/K} multiple-server, finite-capacity queueing station;
+
+@item @math{M/G/1} single-server with general service time distribution;
+
+@item @math{M/H_m/1} single-server with hyperexponential service time distribution.
+
+@end itemize
+
+@end iftex
+
+The functions which analyze the queues above can be used as building
+blocks for analyzing Queueing Networks. For example, Jackson networks
+can be solved by computing the aggregate arrival rates to each node,
+and then solving each node in isolation as if it were a single station
+queueing system.
+
+@c
+@c M/M/1
+@c
+@node The M/M/1 System
+@section The @math{M/M/1} System
+
+The @math{M/M/1} system is made of a single server connected to an
+unlimited FCFS queue. The mean arrival rate is Poisson with arrival
+rate @math{\lambda}; the service time is exponentially distributed
+with average service rate @math{\mu}. The system is stable if
+@math{\lambda < \mu}.
+
+@DOCSTRING(qnmm1)
+
+@noindent @strong{REFERENCES}
+
+@noindent G. Bolch, S. Greiner, H. de Meer and K. Trivedi, @cite{Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications}, Wiley, 1998, Section 6.3.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@c
+@c M/M/m
+@c
+@node The M/M/m System
+@section The @math{M/M/m} System
+
+The @math{M/M/m} system is similar to the @math{M/M/1} system, except
+that there are @math{m \geq 1} identical servers connected to a single
+queue. Thus, at most @math{m} requests can be served at the same
+time. The @math{M/M/m} system can be seen as a single server with
+load-dependent service rate @math{\mu(n)}, which is a function of the
+number @math{n} of nodes in the center:
+
+@example
+@code{mu(n) = min(m,n)*mu}
+@end example
+
+@DOCSTRING(qnmmm)
+
+@noindent @strong{REFERENCES}
+
+@noindent G. Bolch, S. Greiner, H. de Meer and K. Trivedi, @cite{Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications}, Wiley, 1998, Section 6.5.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@c
+@c M/M/inf
+@c
+@node The M/M/inf System
+@section The @math{M/M/}inf System
+
+The @math{M/M/\infty} system is similar to the @math{M/M/m} system,
+except that there are infinitely many identical servers (that is,
+@math{m = \infty}). Each new request is assigned to a new server, so
+that queueing never occurs. The @math{M/M/\infty} system is always
+stable.
+
+@DOCSTRING(qnmminf)
+
+@noindent @strong{REFERENCES}
+
+@noindent G. Bolch, S. Greiner, H. de Meer and K. Trivedi, @cite{Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications}, Wiley, 1998, Section 6.4.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@c
+@c M/M/1/k
+@c
+@node The M/M/1/K System
+@section The @math{M/M/1/K} System 
+
+In a @math{M/M/1/K} finite capacity system there can be at most
+@math{k} jobs at any time. If a new request tries to join the system
+when there are already @math{K} other requests, the arriving request
+is lost. The queue has @math{K-1} slots. The @math{M/M/1/K} system is
+always stable, regardless of the arrival and service rates
+@math{\lambda} and @math{\mu}.
+
+@DOCSTRING(qnmm1k)
+
+@c
+@c M/M/m/k
+@c
+@node The M/M/m/K System
+@section The @math{M/M/m/K} System 
+
+The @math{M/M/m/K} finite capacity system is similar to the
+@math{M/M/1/k} system except that the number of servers is @math{m},
+where @math{1 \leq m \leq K}. The queue is made of @math{K-m}
+slots. The @math{M/M/m/K} system is always stable.
+
+@DOCSTRING(qnmmmk)
+
+@noindent @strong{REFERENCES}
+
+@noindent G. Bolch, S. Greiner, H. de Meer and K. Trivedi, @cite{Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications}, Wiley, 1998, Section 6.6.
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@DOCSTRING(qnmmmk_alt)
+
+@c
+@c Approximate M/M/m
+@c
+@node The Asymmetric M/M/m System
+@section The Asymmetric @math{M/M/m} System 
+
+The Asymmetric @math{M/M/m} system contains @math{m} servers connected
+to a single queue. Differently from the @math{M/M/m} system, in the
+asymmetric @math{M/M/m} each server may have a different service time.
+
+@DOCSTRING(qnammm)
+
+@noindent @strong{REFERENCES}
+
+@noindent G. Bolch, S. Greiner, H. de Meer and K. Trivedi, @cite{Queueing Networks
+and Markov Chains: Modeling and Performance Evaluation with Computer
+Science Applications}, Wiley, 1998
+
+@auindex Bolch, G.
+@auindex Greiner, S.
+@auindex de Meer, H.
+@auindex Trivedi, K.
+
+@c
+@c
+@c
+@node The M/G/1 System
+@section The @math{M/G/1} System 
+
+@DOCSTRING(qnmg1)
+
+@c
+@c
+@c
+@node The M/Hm/1 System
+@section The @math{M/H_m/1} System
+@DOCSTRING(qnmh1)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/doc/summary.txi	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,126 @@
+@c -*- texinfo -*-
+
+@c Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+@c
+@c This file is part of the queueing toolbox, a Queueing Networks
+@c analysis package for GNU Octave.
+@c
+@c The queueing toolbox is free software; you can redistribute it
+@c and/or modify it under the terms of the GNU General Public License
+@c as published by the Free Software Foundation; either version 3 of
+@c the License, or (at your option) any later version.
+@c
+@c The queueing toolbox is distributed in the hope that it will be
+@c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+@c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+@c GNU General Public License for more details.
+@c
+@c You should have received a copy of the GNU General Public License
+@c along with the queueing toolbox; see the file COPYING.  If not, see
+@c <http://www.gnu.org/licenses/>.
+
+@node Summary
+@chapter Summary
+
+This document describes the @code{queueing} toolbox for GNU Octave
+(@code{queueing} in short). The @code{queueing} toolbox, previously
+known as @code{qnetworks}, is a collection of functions written in GNU
+Octave for analyzing queueing networks and Markov
+chains. Specifically, @code{queueing} contains functions for analyzing
+Jackson networks, open, closed or mixed product-form BCMP networks,
+and computation of performance bounds. The following algorithms have
+been implemented
+
+@itemize
+
+@item Convolution for closed, single-class product-form networks
+with load-dependent service centers;
+
+@item Exact and approximate Mean Value Analysis (MVA) for single and
+multiple class product-form closed networks;
+
+@item MVA for mixed, multiple class product-form networks
+with load-independent service centers;
+
+@item Approximate MVA for closed, single-class networks with blocking
+(MVABLO algorithm by F. Akyildiz);
+
+@item Computation of Asymptotic Bounds, Balanced System Bounds
+and Geometric Bounds;
+
+@end itemize
+
+@noindent @code{queueing} 
+provides functions for analyzing the following kind of single-station
+queueing systems:
+
+@itemize
+
+@item @math{M/M/1}
+@item @math{M/M/m}
+@item @math{M/M/\infty}
+@item @math{M/M/1/k} single-server, finite capacity system
+@item @math{M/M/m/k} multiple-server, finite capacity system
+@item Asymmetric @math{M/M/m}
+@item @math{M/G/1} (general service time distribution)
+@item @math{M/H_m/1} (Hyperexponential service time distribution)
+@end itemize
+
+Functions for Markov chain analysis are also provided (discrete and
+continuous time Markov chains are supported):
+
+@itemize
+
+@item Birth-death process;
+@item Computation of transient and steady-state occupancy probabilities;
+@item Computation of mean time to absorption;
+@item Computation of time-averages sojourn time.
+@item Computation of mean passage times
+
+@end itemize
+
+The @code{queueing} toolbox is distributed under the terms of the GNU
+General Public License (GPL), version 3 or later
+(@pxref{Copying}). You are encouraged to share this software with
+others, and make this package more useful by contributing additional
+functions and reporting problems. @xref{Contributing Guidelines}.
+
+If you use the @code{queueing} toolbox in a technical paper, please
+cite it as:
+
+@quotation
+Moreno Marzolla, @emph{The qnetworks Toolbox: A Software Package for
+Queueing Networks Analysis}. Khalid Al-Begain, Dieter Fiems and
+William J. Knottenbelt, Editors, Proceedings 17th International
+Conference on Analytical and Stochastic Modeling Techniques and
+Applications (ASMTA 2010) Cardiff, UK, June 14--16, 2010, volume 6148
+of Lecture Notes in Computer Science, Springer, pp. 102--116, ISBN
+978-3-642-13567-5
+@end quotation
+
+If you use BibTeX, this is the citation block:
+
+@verbatim
+@inproceedings{queueing,
+  author    = {Moreno Marzolla},
+  title     = {The qnetworks Toolbox: A Software Package for Queueing 
+               Networks Analysis},
+  booktitle = {Analytical and Stochastic Modeling Techniques and 
+               Applications, 17th International Conference, 
+               ASMTA 2010, Cardiff, UK, June 14-16, 2010. Proceedings},
+  editor    = {Khalid Al-Begain and Dieter Fiems and William J. Knottenbelt},
+  year      = {2010},
+  publisher = {Springer},
+  series    = {Lecture Notes in Computer Science},
+  volume    = {6148},
+  pages     = {102--116},
+  ee        = {http://dx.doi.org/10.1007/978-3-642-13568-2_8},
+  isbn      = {978-3-642-13567-5}
+}
+@end verbatim
+
+An early draft of the paper above is available as Technical Report
+@uref{http://www.informatica.unibo.it/ricerca/ublcs/2010/UBLCS-2010-04,
+UBLCS-2010-04}, February 2010, Department of Computer Science,
+University of Bologna, Italy.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/Makefile	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,18 @@
+DISTFILES=$(wildcard *.m) Makefile
+
+.PHONY: clean distclean check
+
+ALL: done
+
+done:
+	octave -q grabdemo.m && touch done
+
+dist:
+	ln $(DISTFILES) ../`cat ../fname`/examples/
+
+clean:
+	\rm -f demo_*.m *~ done
+
+distclean: clean
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_ctmc.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,3 @@
+ Q = [ -1  1; \
+        1 -1  ];
+ q = ctmc(Q)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_ctmc_exps.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,16 @@
+ lambda = 0.5;
+ N = 4;
+ birth = lambda*linspace(1,N-1,N-1);
+ death = zeros(1,N-1);
+ Q = diag(birth,1)+diag(death,-1);
+ Q -= diag(sum(Q,2));
+ tt = linspace(0,10,100);
+ p0 = zeros(1,N); p0(1)=1;
+ L = ctmc_exps(Q,tt,p0);
+ plot( tt, L(:,1), ";State 1;", "linewidth", 2, \
+       tt, L(:,2), ";State 2;", "linewidth", 2, \
+       tt, L(:,3), ";State 3;", "linewidth", 2, \
+       tt, L(:,4), ";State 4 (absorbing);", "linewidth", 2);
+ legend("location","northwest");
+ xlabel("Time");
+ ylabel("Expected sojourn time");
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_ctmc_fpt.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,5 @@
+ Q = [ -1.0  0.9  0.1; \
+        0.1 -1.0  0.9; \
+        0.9  0.1 -1.0 ];
+ M = ctmc_fpt(Q)
+ m = ctmc_fpt(Q,1,3)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_ctmc_mtta.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,5 @@
+ mu = 0.01;
+ death = [ 3 4 5 ] * mu;
+ Q = diag(death,-1);
+ Q -= diag(sum(Q,2));
+ t = ctmc_mtta(Q,[0 0 0 1])
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_ctmc_taexps.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,16 @@
+ lambda = 0.5;
+ N = 4;
+ birth = lambda*linspace(1,N-1,N-1);
+ death = zeros(1,N-1);
+ Q = diag(birth,1)+diag(death,-1);
+ Q -= diag(sum(Q,2));
+ t = linspace(1e-3,50,500);
+ p = zeros(1,N); p(1)=1;
+ M = ctmc_taexps(Q,t,p);
+ plot(t, M(:,1), ";State 1;", "linewidth", 2, \
+      t, M(:,2), ";State 2;", "linewidth", 2, \
+      t, M(:,3), ";State 3;", "linewidth", 2, \
+      t, M(:,4), ";State 4 (absorbing);", "linewidth", 2 );
+ legend("location","east");
+ xlabel("Time");
+ ylabel("Time-averaged Expected sojourn time");
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_dtmc.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,14 @@
+ a = 0.2;
+ b = 0.15;
+ P = [ 1-a a; b 1-b];
+ T = 0:14;
+ pp = zeros(2,length(T));
+ for i=1:length(T)
+   pp(:,i) = dtmc(P,T(i),[1 0]);
+ endfor
+ ss = dtmc(P); # compute steady state probabilities
+ plot( T, pp(1,:), "b+;p_0(t);", "linewidth", 2, \
+       T, ss(1)*ones(size(T)), "b;Steady State;", \
+       T, pp(2,:), "r+;p_1(t);", "linewidth", 2, \
+       T, ss(2)*ones(size(T)), "r;Steady State;" );
+ xlabel("Time Step");
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_dtmc_fpt.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,4 @@
+ P = [ 0.0 0.9 0.1; \
+       0.1 0.0 0.9; \
+       0.9 0.1 0.0 ];
+ M = dtmc_fpt(P);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnclosed.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,27 @@
+ P = [0 0.3 0.7; 1 0 0; 1 0 0]; # Transition probability matrix
+ S = [1 0.6 0.2]; # Average service times
+ m = ones(1,3); # All centers are single-server
+ Z = 2; # External delay
+ N = 15; # Maximum population to consider
+
+ V = qnvisits(P); # Compute number of visits from P
+ D = V .* S; # Compute service demand from S and V
+ X_bsb_lower = X_bsb_upper = zeros(1,N);
+ X_ab_lower = X_ab_upper = zeros(1,N);
+ X_mva = zeros(1,N);
+ for n=1:N
+   [X_bsb_lower(n) X_bsb_upper(n)] = qnclosedbsb(n, D, Z);
+   [X_ab_lower(n) X_ab_upper(n)] = qnclosedab(n, D, Z);
+   [U R Q X] = qnclosed( n, S, V, m, Z );
+   X_mva(n) = X(1)/V(1);
+ endfor
+ close all;
+ plot(1:N, X_ab_lower,"g;Asymptotic Bounds;", \
+      1:N, X_bsb_lower,"k;Balanced System Bounds;", \
+      1:N, X_mva,"b;MVA;", "linewidth", 2, \
+      1:N, X_bsb_upper,"k", \
+      1:N, X_ab_upper,"g" );
+ axis([1,N,0,1]);
+ xlabel("Number of Requests n");
+ ylabel("System Throughput X(n)");
+ legend("location","southeast");
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnclosedmultimva.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,31 @@
+ Ntot = 100; # total population size
+ b = linspace(0.1,0.9,10); # fractions of class-1 requests
+ S = [20 80 31 14 23 12; \
+      90 30 33 20 14 7];
+ V = ones(size(S));
+ X1 = X1 = XX = zeros(size(b));
+ R1 = R2 = RR = zeros(size(b));
+ for i=1:length(b)
+   N = [fix(b(i)*Ntot) Ntot-fix(b(i)*Ntot)];
+   # printf("[%3d %3d]\n", N(1), N(2) );
+   [U R Q X] = qnclosedmultimva( N, S, V );
+   X1(i) = X(1,1) / V(1,1);
+   X2(i) = X(2,1) / V(2,1);
+   XX(i) = X1(i) + X2(i);
+   R1(i) = dot(R(1,:), V(1,:));
+   R2(i) = dot(R(2,:), V(2,:));
+   RR(i) = Ntot / XX(i);
+ endfor
+ subplot(2,1,1);
+ plot(b, X1, "linewidth", 2, \
+      b, X2, "linewidth", 2, \
+      b, XX, "linewidth", 2 );
+ legend("location","south");
+ ylabel("Throughput");
+ subplot(2,1,2);
+ plot(b, R1, ";Class 1;", "linewidth", 2, \
+      b, R2, ";Class 2;", "linewidth", 2, \
+      b, RR, ";System;", "linewidth", 2 );
+ legend("location","south");
+ xlabel("Population mix \\beta for Class 1");
+ ylabel("Resp. Time");
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnclosedmultimvaapprox.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,5 @@
+ S = [ 1, 1, 1, 1; 2, 1, 3, 1; 4, 2, 3, 3 ];
+ V = ones(3,4);
+ N = [10 5 1];
+ m = [1 0 1 1];
+ [U R Q X] = qnclosedmultimvaapprox(N,S,V,m);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnclosedsinglemva.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,14 @@
+ S = [ 0.125 0.3 0.2 ];
+ V = [ 16 10 5 ];
+ N = 20;
+ m = ones(1,3);
+ Z = 4;
+ [U R Q X] = qnclosedsinglemva(N,S,V,m,Z);
+ X_s = X(1)/V(1); # System throughput
+ R_s = dot(R,V); # System response time
+ printf("\t    Util      Qlen     RespT      Tput\n");
+ printf("\t--------  --------  --------  --------\n");
+ for k=1:length(S)
+   printf("Dev%d\t%8.4f  %8.4f  %8.4f  %8.4f\n", k, U(k), Q(k), R(k), X(k) );
+ endfor
+ printf("\nSystem\t          %8.4f  %8.4f  %8.4f\n\n", N-X_s*Z, R_s, X_s );
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnclosedsinglemvaapprox.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,23 @@
+ S = [ 0.125 0.3 0.2 ];
+ V = [ 16 10 5 ];
+ N = 30;
+ m = ones(1,3);
+ Z = 4;
+ Xmva = Xapp = Rmva = Rapp = zeros(1,N);
+ for n=1:N
+   [U R Q X] = qnclosedsinglemva(n,S,V,m,Z);
+   Xmva(n) = X(1)/V(1);
+   Rmva(n) = dot(R,V);
+   [U R Q X] = qnclosedsinglemvaapprox(n,S,V,m,Z);
+   Xapp(n) = X(1)/V(1);
+   Rapp(n) = dot(R,V);
+ endfor
+ subplot(2,1,1);
+ plot(1:N, Xmva, ";Exact;", "linewidth", 2, 1:N, Xapp, "x;Approximate;", "markersize", 7);
+ legend("location","southeast");
+ ylabel("Throughput X(n)");
+ subplot(2,1,2);
+ plot(1:N, Rmva, ";Exact;", "linewidth", 2, 1:N, Rapp, "x;Approximate;", "markersize", 7);
+ legend("location","southeast");
+ ylabel("Response Time R(n)");
+ xlabel("Number of Requests n");
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnconvolution.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,13 @@
+ k = [1 2 0];
+ K = sum(k); # Total population size
+ S = [ 1/0.8 1/0.6 1/0.4 ];
+ m = [ 2 3 1 ];
+ V = [ 1 .667 .2 ];
+ [U R Q X G] = qnconvolution( K, S, V, m );
+ p = [0 0 0]; # initialize p
+ # Compute the probability to have k(i) jobs at service center i
+ for i=1:3
+   p(i) = (V(i)*S(i))^k(i) / G(K+1) * \
+          (G(K-k(i)+1) - V(i)*S(i)*G(K-k(i)) );
+   printf("k(%d)=%d prob=%f\n", i, k(i), p(i) );
+ endfor
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnmmm.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,13 @@
+ disp("This is figure 6.4 on p. 220 Bolch et al.");
+ rho = 0.9;
+ ntics = 21;
+ lambda = 0.9;
+ m = linspace(1,ntics,ntics);
+ mu = lambda./(rho .* m);
+ [U R Q X] = qnmmm(lambda, mu, m);
+ qlen = X.*(R-1./mu);
+ plot(m,Q,"o",qlen,"*");
+ axis([0,ntics,0,25]);
+ legend("Jobs in the system","Queue Length","location","northwest");
+ xlabel("Number of servers (m)");
+ title("\lambda = 0.9, \mu = 0.9");
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnopensingle.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,6 @@
+ lambda = 3;
+ V = [16 7 8];
+ S = [0.01 0.02 0.03];
+ [U R Q X] = qnopensingle( lambda, S, V );
+ R_s = dot(R,V) # System response time
+ N = sum(Q) # Average number in system
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnsolve.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,7 @@
+ QQ = { qnmknode( "m/m/m-fcfs", [0.2 0.1 0.1; 0.2 0.1 0.1] ), \
+        qnmknode( "-/g/1-ps", [0.4; 0.6] ), \
+        qnmknode( "-/g/inf", [1; 2] ) };
+ V = [ 1 0.6 0.4; \
+       1 0.3 0.7 ];
+ N = [ 2 1 ];
+ [U R Q X] = qnsolve( "closed", N, QQ, V );
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_1_qnvisits.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,9 @@
+ P = [ 0 0.4 0.6 0; \
+       0.2 0 0.2 0.6; \
+       0 0 0 1; \
+       0 0 0 0 ];
+ lambda = [0.1 0 0 0.3];
+ V = qnvisits(P,lambda);
+ S = [2 1 2 1.8];
+ m = [3 1 1 2];
+ [U R Q X] = qnopensingle( sum(lambda), S, V, m );
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_2_ctmc.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,14 @@
+ a = 0.2;
+ b = 0.15;
+ Q = [ -a a; b -b];
+ T = linspace(0,14,50);
+ pp = zeros(2,length(T));
+ for i=1:length(T)
+   pp(:,i) = ctmc(Q,T(i),[1 0]);
+ endfor
+ ss = ctmc(Q); # compute steady state probabilities
+ plot( T, pp(1,:), "b;p_0(t);", "linewidth", 2, \
+       T, ss(1)*ones(size(T)), "b;Steady State;", \
+       T, pp(2,:), "r;p_1(t);", "linewidth", 2, \
+       T, ss(2)*ones(size(T)), "r;Steady State;" );
+ xlabel("Time");
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_2_ctmc_mtta.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,13 @@
+ N = 100;
+ birth = death = ones(1,N-1); birth(1) = death(N-1) = 0;
+ Q = diag(birth,1)+diag(death,-1); 
+ Q -= diag(sum(Q,2));
+ t = zeros(1,N/2);
+ initial_state = 1:(N/2);
+ for i=initial_state
+   p = zeros(1,N); p(i) = 1;
+   t(i) = ctmc_mtta(Q,p);
+ endfor
+ plot(initial_state,t,"+");
+ xlabel("Initial state");
+ ylabel("MTTA");
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_2_ctmc_taexps.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,34 @@
+ sec = 1;
+ min = sec*60;
+ hour = 60*min;
+ day = 24*hour;
+
+ # state space enumeration {2, RC, RB, 1, 0}
+ a = 1/(10*min);    # 1/a = duration of reboot (10 min)
+ b = 1/(30*sec);    # 1/b = reconfiguration time (30 sec)
+ g = 1/(5000*hour); # 1/g = processor MTTF (5000 hours)
+ d = 1/(4*hour);    # 1/d = processor MTTR (4 hours)
+ c = 0.9;           # coverage
+ Q = [ -2*g 2*c*g 2*(1-c)*g      0  0; \
+          0    -b         0      b  0; \
+          0     0        -a      a  0; \
+          d     0         0 -(g+d)  g; \
+          0     0         0      d -d];
+ p = ctmc(Q);
+ printf("System availability: %f\n",p(1)+p(4));
+ TT = linspace(1e-5,1*day,101);
+ PP = ctmc_taexps(Q,TT,[1 0 0 0 0]);
+ A = At = Abart = zeros(size(TT));
+ A(:) = p(1) + p(4); # steady-state availability
+ for n=1:length(TT)
+   t = TT(n);
+   p = ctmc(Q,t,[1 0 0 0 0]);
+   At(n) = p(1) + p(4); # instantaneous availability
+   Abart(n) = PP(n,1) + PP(n,4); # interval base availability
+ endfor
+ semilogy(TT,A,";Steady-state;", \
+      TT,At,";Instantaneous;", \
+      TT,Abart,";Interval base;");
+ ax = axis();
+ ax(3) = 1-1e-5;
+ axis(ax);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/demo_3_ctmc.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,26 @@
+ sec  = 1;
+ min  = 60*sec;
+ hour = 60*min;
+ day  = 24*hour;
+ year = 365*day;
+ # state space enumeration {2, RC, RB, 1, 0}
+ a = 1/(10*min);    # 1/a = duration of reboot (10 min)
+ b = 1/(30*sec);    # 1/b = reconfiguration time (30 sec)
+ g = 1/(5000*hour); # 1/g = processor MTTF (5000 hours)
+ d = 1/(4*hour);    # 1/d = processor MTTR (4 hours)
+ c = 0.9;           # coverage
+ Q = [ -2*g 2*c*g 2*(1-c)*g      0  0; \
+          0    -b         0      b  0; \
+          0     0        -a      a  0; \
+          d     0         0 -(g+d)  g; \
+          0     0         0      d -d];
+ p = ctmc(Q);
+ A = p(1) + p(4); 
+ printf("System availability   %9.2f min/year\n",A*year/min);
+ printf("Mean time in RB state %9.2f min/year\n",p(3)*year/min);
+ printf("Mean time in RC state %9.2f min/year\n",p(2)*year/min);
+ printf("Mean time in 0 state  %9.2f min/year\n",p(5)*year/min);
+ Q(3,:) = Q(5,:) = 0; # make states 3 and 5 absorbing
+ p0 = [1 0 0 0 0];
+ MTBF = ctmc_mtta(Q, p0) / hour;
+ printf("System MTBF %.2f hours\n",MTBF);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/examples/grabdemo.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,76 @@
+## Copyright (C) 2005, 2006, 2007 David Bateman
+## Modifications Copyright (C) 2009 Moreno Marzolla
+##
+## This file is part of qnetworks. It is based on the fntests.m
+## script included in GNU Octave.
+##
+## Octave 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 3 of the License, or (at
+## your option) any later version.
+##
+## Octave 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+clear all;
+
+global fundirs;
+
+if (nargin == 1)
+  xdir = argv(){1};
+else
+  xdir = "../inst/";
+endif
+
+srcdir = canonicalize_file_name (xdir);
+fundirs = {srcdir};
+
+function print_file_name (nm)
+  filler = repmat (".", 1, 55-length (nm));
+  printf ("  %s %s", nm, filler);
+endfunction
+
+function y = hasdemo (f)
+  fid = fopen (f);
+  str = fscanf (fid, "%s");
+  fclose (fid);
+  y = findstr (str, "%!demo");
+endfunction
+
+function dump_demo( fname, code, idx )
+  if ( !idx) 
+    return;
+  endif
+  printf("%d demos found\n", length(idx)-1 );
+  for i=2:length(idx)
+    demoname = [ "demo_" num2str(i-1) "_" fname ];
+    fid = fopen( demoname, "wt" );
+    fprintf(fid,"%s",code(idx(i-1)+1:idx(i)-1));
+    fclose(fid);
+  endfor
+endfunction
+
+for j=1:length(fundirs)
+  d = fundirs{j};
+  lst = dir (d);
+  for i = 1:length (lst)
+    nm = lst(i).name;
+    if ((length (nm) > 3 && strcmp (nm((end-2):end), ".cc"))
+	 || (length (nm) > 2 && strcmp (nm((end-1):end), ".m")))
+      f = fullfile (d, nm);
+      ## Only run if it contains %!demo
+      if (hasdemo (f))
+	tmp = strrep (f, [srcdir, "/"], "");
+	print_file_name (tmp);
+	[code, idx] = test (f, "grabdemo" );
+        dump_demo( nm, code, idx );
+      endif
+    endif
+  endfor 
+endfor
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/Makefile	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,12 @@
+.PHONY: clean check dist
+DISTFILES=$(wildcard *.m) Makefile
+
+ALL:
+
+clean:
+	\rm -f *~ fntests.log
+
+distclean: clean
+
+dist:
+	ln $(DISTFILES) ../`cat ../fname`/inst/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/ctmc.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,281 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{p} =} ctmc (@var{Q})
+## @deftypefnx {Function File} {@var{p} =} ctmc (@var{Q}, @var{t}. @var{q0})
+##
+## @cindex Markov chain, continuous time
+## @cindex Continuous time Markov chain
+## @cindex Markov chain, state occupancy probabilities
+## @cindex Stationary probabilities
+##
+## With a single argument, compute the stationary state occupancy
+## probability vector @var{p}(1), @dots{}, @var{p}(N) for a
+## Continuous-Time Markov Chain with infinitesimal generator matrix
+## @var{Q} of size  @math{N \times N}. With three arguments, compute the
+## state occupancy probabilities @var{p}(1), @dots{}, @var{p}(N) at time
+## @var{t}, given initial state occupancy probabilities @var{p0} at time
+## 0.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item Q
+## Infinitesimal generator matrix. @var{Q} is a @math{N \times N} square
+## matrix where @code{@var{Q}(i,j)} is the transition rate from state
+## @math{i} to state @math{j}, for @math{1 @leq{} i \neq j @leq{} N}.
+## Transition rates must be nonnegative, and @math{\sum_{j=1}^N Q_{i j} = 0}
+##
+## @item t
+## Time at which to compute the transient probability
+##
+## @item p0
+## @code{@var{p0}(i)} is the probability that the system
+## is in state @math{i} at time 0 .
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item p
+## If this function is invoked with a single argument,
+## @code{@var{p}(i)} is the steady-state probability that the system is
+## in state @math{i}, @math{i = 1, @dots{}, N}. The vector @var{p}
+## satisfies the equation @math{p{\bf Q} = 0} and @math{\sum_{i=1}^N p_i = 1}.
+## If this function is invoked with three arguments, @code{@var{p}(i)}
+## is the probability that the system is in state @math{i} at time @var{t},
+## given the initial occupancy probabilities @var{q0}.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function q = ctmc( Q, t, q0 )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin < 1 || nargin > 3 )
+    print_usage();
+  endif
+
+  issquare(Q) || \
+      usage( "Q must be a square matrix" );
+
+  N = rows(Q);
+  
+  ( norm( sum(Q,2), "inf" ) < epsilon ) || \
+      usage( "Q is not an infinitesimal generator matrix" );
+
+  if ( nargin > 1 ) 
+    ( isscalar(t) && t>=0 ) || \
+        usage("t must be nonnegative");
+  endif
+
+  if ( nargin > 2 )
+    ( isvector(q0) && length(q0) == N && all(q0>=0) && abs(sum(q0)-1.0)<epsilon ) || \
+        usage( "q0 must be a probability vector" );   
+    q0 = q0(:)'; # make q0 a row vector
+  else
+    q0 = ones(1,N) / N;
+  endif
+
+  if ( nargin == 1 )
+    q = __ctmc_steady_state( Q );
+  else
+    q = __ctmc_transient(Q, t, q0 );
+  endif
+
+endfunction
+
+## Helper function, compute steady state probability
+function q = __ctmc_steady_state( Q )
+  persistent epsilon = 10*eps;
+  N = rows(Q);
+
+  ## non zero columns
+  nonzero=find( any(abs(Q)>epsilon,1 ) );
+  if ( length(nonzero) == 0 )
+    error( "Q is the zero matrix" );
+  endif
+
+  normcol = nonzero(1); # normalization condition column
+
+  ## force probability of unvisited states to zero
+  for i=find( all(abs(Q)<epsilon,1) )
+    Q(i,i) = 1;
+  endfor
+
+  ## assert( rank(Q) == N-1 );
+
+  Q(:,normcol) = 1; # add normalization condition
+  b = zeros(1,N); b(normcol)=1;
+  q = b/Q; # qQ = b;
+endfunction
+
+## Helper function, compute transient probability
+function q = __ctmc_transient( Q, t, q0 )
+  q = q0*expm(Q*t);
+endfunction
+
+%!test
+%! Q = [-1 1 0 0; 2 -3 1 0; 0 2 -3 1; 0 0 2 -2];
+%! q = ctmc(Q);
+%! assert( q*Q, 0*q, 1e-5 );
+%! assert( q, [8/15 4/15 2/15 1/15], 1e-5 );
+
+## test failure patterns
+%!test
+%! fail( "ctmc([1 1; 1 1])", "infinitesimal" );
+%! fail( "ctmc([1 1 1; 1 1 1])", "square" );
+
+## test unvisited state.
+%!test
+%! Q = [0  0  0; ...
+%!      0 -1  1; ...
+%!      0  1 -1];
+%! q = ctmc(Q);
+%! assert( q*Q, 0*q, 1e-5 );
+%! assert( q, [ 0 0.5 0.5 ], 1e-5 );
+
+## Example 3.1 p. 123 Bolch et al.
+%!test
+%! lambda = 1;
+%! mu = 2;
+%! Q = [ -lambda lambda 0 0   ; ...
+%!       mu -(lambda+mu) lambda 0  ; ...
+%!       0 mu -(lambda+mu) lambda ; ...
+%!       0 0 mu -mu ];
+%! q = ctmc(Q);
+%! assert( q, [8/15 4/15 2/15 1/15], 1e-5 );
+
+## Example 3.4 p. 138 Bolch et al.
+%!test
+%! Q = [ -1 0.4 0.6 0 0 0; ...
+%!       2 -3 0 0.4 0.6 0; ...
+%!       3 0 -4 0 0.4 0.6; ...
+%!       0 2 0 -2 0 0; ...
+%!       0 3 2 0 -5 0; ...
+%!       0 0 3 0 0 -3 ];
+%! q = ctmc(Q);
+%! assert( q, [0.6578 0.1315 0.1315 0.0263 0.0263 0.0263], 1e-4 );
+
+## Example 3.2 p. 128 Bolch et al.
+%!test
+%! Q = [-1 1 0 0 0 0 0; ...
+%!      0 -3 1 0 2 0 0; ...
+%!      0 0 -3 1 0 2 0; ...
+%!      0 0 0 -2 0 0 2; ...
+%!      2 0 0 0 -3 1 0; ...
+%!      0 2 0 0 0 -3 1; ...
+%!      0 0 2 0 0 0 -2 ];
+%! q = ctmc(Q);
+%! assert( q, [0.2192 0.1644 0.1507 0.0753 0.1096 0.1370 0.1438], 1e-4 );
+
+%!test
+%! a = 0.2;
+%! b = 0.8;
+%! Q = [-a a; b -b];
+%! qlim = ctmc(Q);
+%! q = ctmc(Q, 100, [1 0]);
+%! assert( qlim, q, 1e-5 );
+
+%!demo
+%! Q = [ -1  1; \
+%!        1 -1  ];
+%! q = ctmc(Q)
+
+%!demo
+%! a = 0.2;
+%! b = 0.15;
+%! Q = [ -a a; b -b];
+%! T = linspace(0,14,50);
+%! pp = zeros(2,length(T));
+%! for i=1:length(T)
+%!   pp(:,i) = ctmc(Q,T(i),[1 0]);
+%! endfor
+%! ss = ctmc(Q); # compute steady state probabilities
+%! plot( T, pp(1,:), "b;p_0(t);", "linewidth", 2, \
+%!       T, ss(1)*ones(size(T)), "b;Steady State;", \
+%!       T, pp(2,:), "r;p_1(t);", "linewidth", 2, \
+%!       T, ss(2)*ones(size(T)), "r;Steady State;" );
+%! xlabel("Time");
+
+## This example is from: David I. Heimann, Nitin Mittal, Kishor S. Trivedi,
+## "Availability and Reliability Modeling for Computer Systems", sep 1989,
+## section 2.4.
+## **NOTE** the value of \pi_0 reported in the paper appears to be wrong
+## (it is written as 0.00000012779, but probably should be 0.0000012779).
+%!test
+%! sec = 1;
+%! min = 60*sec;
+%! hour = 60*min;
+%! ## the state space enumeration is {2, RC, RB, 1, 0}
+%! a = 1/(10*min);    # 1/a = duration of reboot (10 min)
+%! b = 1/(30*sec);    # 1/b = reconfiguration time (30 sec)
+%! g = 1/(5000*hour); # 1/g = processor MTTF (5000 hours)
+%! d = 1/(4*hour);    # 1/d = processor MTTR (4 hours)
+%! c = 0.9;           # coverage
+%! Q = [ -2*g 2*c*g 2*(1-c)*g      0  0 ; \
+%!          0    -b         0      b  0 ; \
+%!          0     0        -a      a  0 ; \
+%!          d     0         0 -(g+d)  g ; \
+%!          0     0         0      d -d];
+%! p = ctmc(Q);
+%! assert( p, [0.9983916, 0.000002995, 0.0000066559, 0.00159742, 0.0000012779], 1e-6 );
+%! Q(3,:) = Q(5,:) = 0; # make states 3 and 5 absorbing
+%! p0 = [1 0 0 0 0];
+%! MTBF = ctmc_mtta(Q, p0) / hour;
+%! assert( fix(MTBF), 24857);
+
+## This example is from: David I. Heimann, Nitin Mittal, Kishor S. Trivedi,
+## "Availability and Reliability Modeling for Computer Systems", sep 1989,
+## section 2.5
+%!demo
+%! sec  = 1;
+%! min  = 60*sec;
+%! hour = 60*min;
+%! day  = 24*hour;
+%! year = 365*day;
+%! # state space enumeration {2, RC, RB, 1, 0}
+%! a = 1/(10*min);    # 1/a = duration of reboot (10 min)
+%! b = 1/(30*sec);    # 1/b = reconfiguration time (30 sec)
+%! g = 1/(5000*hour); # 1/g = processor MTTF (5000 hours)
+%! d = 1/(4*hour);    # 1/d = processor MTTR (4 hours)
+%! c = 0.9;           # coverage
+%! Q = [ -2*g 2*c*g 2*(1-c)*g      0  0; \
+%!          0    -b         0      b  0; \
+%!          0     0        -a      a  0; \
+%!          d     0         0 -(g+d)  g; \
+%!          0     0         0      d -d];
+%! p = ctmc(Q);
+%! A = p(1) + p(4); 
+%! printf("System availability   %9.2f min/year\n",A*year/min);
+%! printf("Mean time in RB state %9.2f min/year\n",p(3)*year/min);
+%! printf("Mean time in RC state %9.2f min/year\n",p(2)*year/min);
+%! printf("Mean time in 0 state  %9.2f min/year\n",p(5)*year/min);
+%! Q(3,:) = Q(5,:) = 0; # make states 3 and 5 absorbing
+%! p0 = [1 0 0 0 0];
+%! MTBF = ctmc_mtta(Q, p0) / hour;
+%! printf("System MTBF %.2f hours\n",MTBF);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/ctmc_bd.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,85 @@
+## Copyright (C) 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{p} =} ctmc_bd (@var{birth}, @var{death})
+##
+## @cindex Markov chain, continuous time
+## @cindex Birth-death process
+##
+## Compute the steady-state solution of a birth-death process with state
+## space @math{(1, @dots{}, N)}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item birth
+## Vector with @math{N-1} elements, where @code{@var{birth}(i)} is the
+## transition rate from state @math{i} to state @math{i+1}.
+##
+## @item death
+## Vector with @math{N-1} elements, where @code{@var{death}(i)} is the
+## transition rate from state @math{i+1} to state @math{i}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item p
+## @code{@var{p}(i)} is the steady-state probability that the system is
+## in state @math{i}, @math{i=1, @dots{}, N}.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function p = ctmc_bd( birth, death )
+
+  if ( nargin != 2 ) 
+    print_usage();
+  endif
+
+  ( isvector( birth ) && isvector( death ) ) || \
+      usage( "birth and death must be vectors" );
+  birth = birth(:); # make birth a column vector
+  death = death(:); # make death a column vector
+  size_equal( birth, death ) || \
+      usage( "birth and death must have the same length" );
+  all( birth >= 0 ) || \
+      usage( "birth must be >= 0" );
+  all( death >= 0 ) || \
+      usage( "death must be >= 0" );
+
+  n = length(birth) + 1; # number of states 
+
+  ## builds the transition probability matrix
+  Q = diag( birth, 1 ) + diag( death, -1 );
+  Q -= diag( sum(Q,2) );
+  p = ctmc( Q );
+endfunction
+%!test
+%! birth = [ 1 1 1 ];
+%! death = [ 2 2 2 ];
+%! result = ctmc_bd( birth, death );
+%! assert( result, [ 8/15 4/15 2/15 1/15 ], 1e-5 );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/ctmc_bd_solve.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,35 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{p} =} ctmc_bd_solve (@var{birth}, @var{death})
+##
+## This function is deprecated and will be removed from future versions
+## of the @code{queueing} toolbox. Please use @code{ctmc_bd()} instead.
+##
+## @seealso{ctmc_bd}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function p = ctmc_bd_solve( birth, death )
+  warning("Function ctmc_db_solve() is deprecated and will be removed in the future. Please use ctmc_bd() instead.");
+  p = ctmc_bd(birth, death);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/ctmc_exps.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,108 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{L} =} ctmc_exps (@var{Q}, @var{tt}, @var{p})
+##
+## @cindex Markov chain, continuous time
+## @cindex Expected sojourn time
+##
+## Compute the expected total time @code{@var{L}(t,j)} spent in state
+## @math{j} during the time interval @code{[0,@var{tt}(t))}, assuming
+## that at time 0 the state occupancy probability was @var{p}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item Q
+## Infinitesimal generator matrix. @code{@var{Q}(i,j)} is the transition
+## rate from state @math{i} to state @math{j},
+## @math{1 @leq{} i \neq j @leq{} N}. The matrix @var{Q} must also satisfy the
+## condition @code{sum(@var{Q},2) == 0}
+##
+## @item tt
+## This parameter is a vector used for numerical integration. The first
+## element @code{@var{tt}(1)} must be 0, and the last element
+## @code{@var{tt}(end)} must be the upper bound of the interval
+## @math{[0,t)} of interest (@code{@var{tt}(end) == @math{t}}).
+##
+## @item p
+## @code{@var{p}(i)} is the probability that at time 0 the system was in
+## state @math{i}, for all @math{i = 1, @dots{}, N}
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item L
+## @code{@var{L}(t,j)} is the expected time spent in state @math{j}
+## during the interval @code{[0,@var{tt}(t))}. @code{1 @leq{} @var{t} @leq{} length(@var{tt})}
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function L = ctmc_exps( Q, tt, p )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin != 3 )
+    print_usage();
+  endif
+
+  issquare(Q) || \
+      usage( "Q must be a square matrix" );
+
+  ( norm( sum(Q,2), "inf" ) < epsilon ) || \
+      error( "Q is not an infinitesimal generator matrix" );
+
+  ( isvector(p) && length(p) == size(Q,1) && all(p>=0) && abs(sum(p)-1.0)<epsilon ) || \
+      usage( "p must be a probability vector" );
+
+  ( isvector(tt) && abs(tt(1)) < epsilon ) || \
+      usage( "tt must be a vector, and tt(1) must be 0.0" );
+  tt = tt(:)'; # make tt a row vector
+  p = p(:)'; # make p a row vector
+  ff = @(x,t) (x(:)'*Q+p);
+  fj = @(x,t) (Q);
+  L = lsode( {ff, fj}, p, tt );
+endfunction
+
+%!demo
+%! lambda = 0.5;
+%! N = 4;
+%! birth = lambda*linspace(1,N-1,N-1);
+%! death = zeros(1,N-1);
+%! Q = diag(birth,1)+diag(death,-1);
+%! Q -= diag(sum(Q,2));
+%! tt = linspace(0,10,100);
+%! p0 = zeros(1,N); p0(1)=1;
+%! L = ctmc_exps(Q,tt,p0);
+%! plot( tt, L(:,1), ";State 1;", "linewidth", 2, \
+%!       tt, L(:,2), ";State 2;", "linewidth", 2, \
+%!       tt, L(:,3), ";State 3;", "linewidth", 2, \
+%!       tt, L(:,4), ";State 4 (absorbing);", "linewidth", 2);
+%! legend("location","northwest");
+%! xlabel("Time");
+%! ylabel("Expected sojourn time");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/ctmc_fpt.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,128 @@
+## Copyright (C) 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{M} =} ctmc_fpt (@var{Q})
+## @deftypefnx {Function File} {@var{m} =} ctmc_fpt (@var{Q}, @var{i}, @var{j})
+##
+## @cindex Markov chain, continuous time
+## @cindex First passage times
+##
+## If called with a single argument, computes the mean first passage
+## times @code{@var{M}(i,j)}, the average times before state @var{j} is
+## reached, starting from state @var{i}, for all @math{1 \leq i, j \leq
+## N}. If called with three arguments, returns the single value
+## @code{@var{m} = @var{M}(i,j)}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item Q
+## Infinitesimal generator matrix. @var{Q} is a @math{N \times N} square
+## matrix where @code{@var{Q}(i,j)} is the transition rate from state
+## @math{i} to state @math{j}, for @math{1 @leq{} i \neq j @leq{} N}.
+## Transition rates must be nonnegative, and @math{\sum_{j=1}^N Q_{i j} = 0}
+##
+## @item i
+## Initial state.
+##
+## @item j
+## Destination state. If @var{j} is a vector, returns the mean first passage
+## time to any state in @var{j}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item M
+## If this function is called with a single argument, the result
+## @code{@var{M}(i,j)} is the average time before state
+## @var{j} is visited for the first time, starting from state @var{i}.
+##
+## @item m
+## If this function is called with three arguments, the result
+## @var{m} is the average time before state @var{j} is visited for the first 
+## time, starting from state @var{i}.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function result = ctmc_fpt( Q, i, j )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin != 1 && nargin != 3 )
+    print_usage();
+  endif
+
+  issquare(Q) || \
+      usage( "Q must be a square matrix" );
+
+  N = rows(Q);
+
+  ( norm( sum(Q,2), "inf" ) < epsilon ) || \
+      usage( "Q is not an infinitesimal generator matrix" );
+
+  if ( nargin == 1 ) 
+    M = zeros(N,N);
+    for j=1:N
+      QQ = Q;
+      QQ(j,:) = 0; # make state j absorbing
+      for i=1:N
+	p0 = zeros(1,N); p0(i) = 1;
+	M(i,j) = ctmc_mtta(QQ,p0);
+      endfor
+    endfor
+    result = M;
+  else
+    (isscalar(i) && i>=1 && j<=N) || usage("i must be an integer in the range 1..%d", N);
+    (isvector(j) && all(j>=1) && all(j<=N)) || usage("j must be an integer or vector with elements in 1..%d", N);
+    j = j(:)'; # make j a row vector
+    Q(j,:) = 0; # make state(s) j absorbing
+    p0 = zeros(1,N); p0(i) = 1;
+    result = ctmc_mtta(Q,p0);    
+  endif
+endfunction
+%!demo
+%! Q = [ -1.0  0.9  0.1; \
+%!        0.1 -1.0  0.9; \
+%!        0.9  0.1 -1.0 ];
+%! M = ctmc_fpt(Q)
+%! m = ctmc_fpt(Q,1,3)
+
+%!test
+%! Q = unifrnd(0.1,0.9,10,10);
+%! Q -= diag(sum(Q,2));
+%! M = ctmc_fpt(Q);
+
+%!test
+%! Q = unifrnd(0.1,0.9,10,10);
+%! Q -= diag(sum(Q,2));
+%! m = ctmc_fpt(Q,1,3);
+
+%!test
+%! Q = unifrnd(0.1,0.9,10,10);
+%! Q -= diag(sum(Q,2));
+%! m = ctmc_fpt(Q,1,[3 5 6]);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/ctmc_mtta.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,140 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{t} =} ctmc_mtta (@var{Q}, @var{p})
+##
+## @cindex Markov chain, continuous time
+## @cindex Mean time to absorption
+##
+## Compute the Mean-Time to Absorption (MTTA) starting from initial
+## occupancy probability @var{p} at time 0. If there are no absorbing
+## states, this function fails with an error.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item Q
+## @math{N \times N} infinitesimal generator matrix. @code{@var{Q}(i,j)}
+## is the transition rate from state @math{i} to state @math{j}, @math{i
+## \neq j}. The matrix @var{Q} must satisfy the condition
+## @math{\sum_{j=1}^N Q_{i j} = 0}
+##
+## @item p
+## @code{@var{p}(i)} is the probability that the system is in state @math{i}
+## at time 0, for each @math{i=1, @dots{}, N}
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item t
+## Mean time to absorption of the process represented by matrix @var{Q}.
+## If there are no absorbing states, this function fails.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function t = ctmc_mtta( Q, p )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin != 2 )
+    print_usage();
+  endif
+
+  issquare(Q) || \
+      usage( "Q must be a square matrix" );
+  
+  N = rows(Q);
+
+  all( abs( sum(Q,2) ) < epsilon )  || \
+      usage( "Q is not an infinitesimal generator matrix" );
+
+  ( isvector(p) && length(p) == N && all(p>=0) && abs(sum(p)-1.0)<epsilon ) || \
+      usage( "p must be a probability vector" );
+
+  ## Find nonzero rows. Nonzero rows correspond to transient states,
+  ## while zero rows are absorbing states. If there are no zero rows,
+  ## then the Markov chain does not contain absorbing states and we
+  ## raise an error
+  nzrows = find( any( abs(Q) > epsilon, 2 ) );
+  if ( length( nzrows ) == N )
+    error( "There are no absorbing states" );
+  endif
+
+  QN = Q(nzrows,nzrows);
+  pN = p(nzrows);
+  L = -pN*inv(QN);
+  t = sum(L);
+endfunction
+%!test
+%! Q = [0 1 0; 1 0 1; 0 1 0 ]; Q -= diag( sum(Q,2) );
+%! fail( "ctmc_mtta(Q,[1 0 0])", "no absorbing");
+
+%!test
+%! Q = [0 1 0; 1 0 1; 0 0 0; 0 0 0 ];
+%! fail( "ctmc_mtta(Q,[1 0 0])", "square matrix");
+
+%!test
+%! Q = [0 1 0; 1 0 1; 0 0 0 ];
+%! fail( "ctmc_mtta(Q,[1 0 0])", "not an infinitesimal");
+
+%!test
+%! Q = [ 0 0.1 0 0; \
+%!       0.9 0 0.1 0; \
+%!       0 0.9 0 0.1; \
+%!       0 0 0 0 ];
+%! Q -= diag( sum(Q,2) );
+%! assert( ctmc_mtta( Q,[0 0 0 1] ), 0 ); # state 4 is absorbing
+
+%!test
+%! Q = [-1 1; 0 0];
+%! assert( ctmc_mtta( Q, [0 1] ), 0 ); # state 2 is absorbing
+%! assert( ctmc_mtta( Q, [1 0] ), 1 ); # the result has been computed by hand
+
+## Compute the MTTA of a pure death process with 4 states
+## (state 1 is absorbing). State 4 is the initial state.
+%!demo
+%! mu = 0.01;
+%! death = [ 3 4 5 ] * mu;
+%! Q = diag(death,-1);
+%! Q -= diag(sum(Q,2));
+%! t = ctmc_mtta(Q,[0 0 0 1])
+
+%!demo
+%! N = 100;
+%! birth = death = ones(1,N-1); birth(1) = death(N-1) = 0;
+%! Q = diag(birth,1)+diag(death,-1); 
+%! Q -= diag(sum(Q,2));
+%! t = zeros(1,N/2);
+%! initial_state = 1:(N/2);
+%! for i=initial_state
+%!   p = zeros(1,N); p(i) = 1;
+%!   t(i) = ctmc_mtta(Q,p);
+%! endfor
+%! plot(initial_state,t,"+");
+%! xlabel("Initial state");
+%! ylabel("MTTA");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/ctmc_solve.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,35 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{q} =} ctmc_solve (@var{Q})
+##
+## This function is deprecated and will be removed from future versions
+## of the @code{queueing} toolbox. Please use @code{ctmc()} instead.
+##
+## @seealso{ctmc}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function q = ctmc_solve( Q )
+  warning("Function ctmc_solve() is deprecated and will be removed in the future. Please use ctmc() instead.");
+  q = ctmc(Q);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/ctmc_taexps.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,152 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{M} =} ctmc_taexps (@var{Q}, @var{tt}, @var{p})
+##
+## @cindex Markov chain, continuous time
+## @cindex Time-alveraged sojourn time
+##
+## Compute the @emph{time-averaged sojourn time} @code{@var{M}(t,j)},
+## defined as the fraction of the time interval @code{[0,@var{tt}(t))} spent in
+## state @math{j}, assuming that at time 0 the state occupancy
+## probability was @var{p}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item Q
+## Infinitesimal generator matrix. @code{@var{Q}(i,j)} is the transition
+## rate from state @math{i} to state @math{j},
+## @math{1 @leq{} i \neq j @leq{} N}. The
+## matrix @var{Q} must also satisfy the condition @code{sum(@var{Q},2) == 0}
+##
+## @item tt
+## This parameter is a vector used for numerical integration of the
+## sujourn time. The first element @code{@var{tt}(1)} must be slightly
+## larger than 0, and the
+## last element @code{@var{tt}(end)} must be the upper limit of the
+## interval @math{[0,t)} of interest (@code{@var{tt}(end) == @math{t}}).
+## This vector is used by the ODE solver to compute the solution
+## @var{M}.
+##
+## @item p
+## @code{@var{p}(i)} is the probability that, at time 0, the system was in
+## state @math{i}, for all @math{i = 1, @dots{}, N}
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item M
+## @code{@var{M}(t,j)} is the expected fraction of time spent in state
+## @math{j} during the interval @math{[0,tt(t))} assuming that the state
+## occupancy probability at time zero was @var{p}. @code{1 @leq{}
+## @var{t} @leq{} length(@var{tt})}
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function M = ctmc_taexps( Q, t, p )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin != 3 )
+    print_usage();
+  endif
+
+  issquare(Q) || \
+      usage( "Q must be a square matrix" );
+
+  N = rows(Q);
+
+  ( norm( sum(Q,2), "inf" ) < epsilon ) || \
+      usage( "Q is not an infinitesimal generator matrix" );
+
+  ( isvector(p) && length(p) == N && all(p>=0) && abs(sum(p)-1.0)<epsilon ) || \
+      usage( "p must be a probability vector" );
+  t = t(:)'; # make t a row vector
+  p = p(:)'; # make p a row vector
+  ff = @(x,t) (((x')*(Q-eye(N)/t).+p/t)');
+  fj = @(x,t) (Q-eye(N)/t);
+  M = lsode( {ff, fj}, zeros(size(p)), t );
+endfunction
+
+%!demo
+%! lambda = 0.5;
+%! N = 4;
+%! birth = lambda*linspace(1,N-1,N-1);
+%! death = zeros(1,N-1);
+%! Q = diag(birth,1)+diag(death,-1);
+%! Q -= diag(sum(Q,2));
+%! t = linspace(1e-3,50,500);
+%! p = zeros(1,N); p(1)=1;
+%! M = ctmc_taexps(Q,t,p);
+%! plot(t, M(:,1), ";State 1;", "linewidth", 2, \
+%!      t, M(:,2), ";State 2;", "linewidth", 2, \
+%!      t, M(:,3), ";State 3;", "linewidth", 2, \
+%!      t, M(:,4), ";State 4 (absorbing);", "linewidth", 2 );
+%! legend("location","east");
+%! xlabel("Time");
+%! ylabel("Time-averaged Expected sojourn time");
+
+## This example is from: David I. Heimann, Nitin Mittal, Kishor S. Trivedi,
+## "Availability and Reliability Modeling for Computer Systems", sep 1989,
+## section 2.5
+%!demo
+%! sec = 1;
+%! min = sec*60;
+%! hour = 60*min;
+%! day = 24*hour;
+%!
+%! # state space enumeration {2, RC, RB, 1, 0}
+%! a = 1/(10*min);    # 1/a = duration of reboot (10 min)
+%! b = 1/(30*sec);    # 1/b = reconfiguration time (30 sec)
+%! g = 1/(5000*hour); # 1/g = processor MTTF (5000 hours)
+%! d = 1/(4*hour);    # 1/d = processor MTTR (4 hours)
+%! c = 0.9;           # coverage
+%! Q = [ -2*g 2*c*g 2*(1-c)*g      0  0; \
+%!          0    -b         0      b  0; \
+%!          0     0        -a      a  0; \
+%!          d     0         0 -(g+d)  g; \
+%!          0     0         0      d -d];
+%! p = ctmc(Q);
+%! printf("System availability: %f\n",p(1)+p(4));
+%! TT = linspace(1e-5,1*day,101);
+%! PP = ctmc_taexps(Q,TT,[1 0 0 0 0]);
+%! A = At = Abart = zeros(size(TT));
+%! A(:) = p(1) + p(4); # steady-state availability
+%! for n=1:length(TT)
+%!   t = TT(n);
+%!   p = ctmc(Q,t,[1 0 0 0 0]);
+%!   At(n) = p(1) + p(4); # instantaneous availability
+%!   Abart(n) = PP(n,1) + PP(n,4); # interval base availability
+%! endfor
+%! semilogy(TT,A,";Steady-state;", \
+%!      TT,At,";Instantaneous;", \
+%!      TT,Abart,";Interval base;");
+%! ax = axis();
+%! ax(3) = 1-1e-5;
+%! axis(ax);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/dtmc.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,159 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{p} =} dtmc (@var{P})
+## @deftypefnx {Function File} {@var{p} =} dtmc (@var{P}, @var{n}, @var{p0})
+##
+## @cindex Markov chain, discrete time
+## @cindex Discrete time Markov chain
+## @cindex Markov chain, stationary probabilities
+## @cindex Stationary probabilities
+##
+## With a single argument, compute the steady-state probability vector
+## @code{@var{p}(1), @dots{}, @var{p}(N)} for a
+## Discrete-Time Markov Chain given the @math{N \times N} transition
+## probability matrix @var{P}. With three arguments, compute the
+## probability vector @code{@var{p}(1), @dots{}, @var{p}(N)}
+## after @var{n} steps, given initial probability vector @var{p0} at
+## time 0.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @code{@var{P}(i,j)} is the transition probability from state @math{i}
+## to state @math{j}. @var{P} must be an irreducible stochastic matrix,
+## which means that the sum of each row must be 1 (@math{\sum_{j=1}^N P_{i j} = 1}), and the rank of
+## @var{P} must be equal to its dimension.
+##
+## @item n
+## Step at which to compute the transient probability
+##
+## @item p0
+## @code{@var{p0}(i)} is the probability that at step 0 the system
+## is in state @math{i}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item p
+## If this function is invoked with a single argument,
+## @code{@var{p}(i)} is the steady-state probability that the system is
+## in state @math{i}. @var{p} satisfies the equations @math{p = p{\bf P}} and @math{\sum_{i=1}^N p_i = 1}. If this function is invoked
+## with three arguments, @code{@var{p}(i)} is the marginal probability
+## that the system is in state @math{i} at step @var{n},
+## given the initial probabilities @code{@var{p0}(i)} that the initial state is
+## @math{i}.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function q = dtmc( P, n, p0 )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin < 1 || nargin > 3 )
+    print_usage();
+  endif
+
+  N = dtmc_check_P(P);
+  
+  if ( nargin > 1 )
+    ( isscalar(n) && n>=0 ) || \
+	usage( "n must be >=0" );
+  endif
+
+  if ( nargin > 2 )
+    ( isvector(p0) && length(p0) == N && all(p0>=0) && abs(sum(p0)-1.0)<epsilon ) || \
+        usage( "p0 must be a probability vector" );   
+    p0 = p0(:)'; # make q0 a row vector
+  else
+    p0 = ones(1,N) / N;
+  endif
+
+  if ( nargin == 1 )
+    q = __dtmc_steady_state( P );
+  else
+    q = __dtmc_transient(P, n, p0);
+  endif
+endfunction
+
+## Helper function, compute steady-state probability
+function q = __dtmc_steady_state( P )
+  N = rows(P);
+  A = P-eye(N);
+  A(:,N) = 1; # add normalization condition
+  rank( A ) == N || \
+      warning( "dtmc(): P is reducible" );
+
+  b = [ zeros(1,N-1) 1 ];
+  q = b/A;
+endfunction
+
+## Helper function, compute transient probability
+function q = __dtmc_transient( P, n, p0 )
+  q = p0*P^n;
+endfunction
+
+%!test
+%! P = [0.75 0.25; 0.5 0.5];
+%! q = dtmc(P);
+%! assert( q*P, q, 1e-5 );
+%! assert( q, [0.6666 0.3333], 1e-4 );
+
+%!test
+%! #Example 2.11 p. 44 Bolch et al.
+%! P = [0.5 0.5; 0.5 0.5];
+%! q = dtmc(P);
+%! assert( q, [0.5 0.5], 1e-3 );
+
+%!test
+%! fail("dtmc( [1 1 1; 1 1 1] )", "square");
+
+%!test
+%! a = 0.2;
+%! b = 0.8;
+%! P = [1-a a; b 1-b];
+%! plim = dtmc(P);
+%! p = dtmc(P, 100, [1 0]);
+%! assert( plim, p, 1e-5 );
+
+%!demo
+%! a = 0.2;
+%! b = 0.15;
+%! P = [ 1-a a; b 1-b];
+%! T = 0:14;
+%! pp = zeros(2,length(T));
+%! for i=1:length(T)
+%!   pp(:,i) = dtmc(P,T(i),[1 0]);
+%! endfor
+%! ss = dtmc(P); # compute steady state probabilities
+%! plot( T, pp(1,:), "b+;p_0(t);", "linewidth", 2, \
+%!       T, ss(1)*ones(size(T)), "b;Steady State;", \
+%!       T, pp(2,:), "r+;p_1(t);", "linewidth", 2, \
+%!       T, ss(2)*ones(size(T)), "r;Steady State;" );
+%! xlabel("Time Step");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/dtmc_check_P.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,56 @@
+## Copyright (C) 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{result} =} dtmc_check_P (@var{P})
+##
+## @cindex Markov chain, discrete time
+##
+## Returns the size (number of rows or columns) of square matrix
+## @var{P}, if and only if @var{P} is a valid transition probability
+## matrix. This means that (i) all elements of @var{P} must be nonnegative, 
+## and (ii) the sum of each row must be 1.0. 
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function result = dtmc_check_P( P )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin != 1 )
+    print_usage();
+  endif
+
+  issquare(P) || \
+      usage( "P must be a square matrix" );
+  
+  ( all(all(P >= 0) ) && norm( sum(P,2) - 1, "inf" ) < epsilon ) || \
+      error( "P is not a stochastic matrix" );
+
+  result = rows(P);
+endfunction
+%!test
+%! fail("dtmc_check_P( [1 1 1; 1 1 1] )", "square");
+%! fail("dtmc_check_P( [1 0 0; 0 0.5 0; 0 0 0] )", "stochastic" );
+
+%!test
+%! P = [0 1; 1 0];
+%! assert( dtmc_check_P(P), 2 );
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/dtmc_fpt.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,150 @@
+## Copyright (C) 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{M} =} dtmc_fpt (@var{P})
+## @deftypefnx {Function File} {@var{m} =} dtmc_fpt (@var{P}, @var{i}, @var{j})
+##
+## @cindex Markov chain, discrete time
+## @cindex First passage times
+##
+## If called with a single argument, computes the mean first passage
+## times @code{@var{M}(i,j)}, that are the average number of transitions before
+## state @var{j} is reached, starting from state @var{i}, for all
+## @math{1 \leq i, j \leq N}. If called with three arguments, returns
+## the single value @code{@var{m} = @var{M}(i,j)}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @code{@var{P}(i,j)} is the transition probability from state @math{i}
+## to state @math{j}. @var{P} must be an irreducible stochastic matrix,
+## which means that the sum of each row must be 1 (@math{\sum_{j=1}^N
+## P_{i j} = 1}), and the rank of @var{P} must be equal to its
+## dimension.
+##
+## @item i
+## Initial state.
+##
+## @item j
+## Destination state. If @var{j} is a vector, returns the mean first passage
+## time to any state in @var{j}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item M
+## If this function is called with a single argument, the result
+## @code{@var{M}(i,j)} is the average number of transitions before state
+## @var{j} is reached for the first time, starting from state @var{i}.
+##
+## @item m
+## If this function is called with three arguments, the result @var{m}
+## is the average number of transitions before state @var{j} is visited
+## for the first time, starting from state @var{i}.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function result = dtmc_fpt( P, i, j )
+  persistent epsilon = 10*eps;
+
+  if ( nargin != 1 && nargin != 3)
+    print_usage();
+  endif
+
+  issquare(P) || \
+      usage( "P must be a square matrix" );
+
+  N = rows(P);
+  
+  ( all(P >= 0 ) && norm( sum(P,2) - 1, "inf" ) < epsilon ) || \
+      usage( "P is not a stochastic matrix" );
+
+  if ( nargin == 1 )   
+    M = zeros(N,N);
+    ## M(i,j) = 1 + sum_{k \neq j} P(i,k) M(k,j)
+    b = ones(N,1);
+    for j=1:N
+      A = -P;
+      A(:,j) = 0;
+      A += eye(N,N);
+      res = A \ b;
+      M(:,j) = res';
+    endfor
+    result = M;
+  else
+    (isscalar(i) && i>=1 && j<=N) || usage("i must be an integer in the range [1,%d]", N);
+    (isvector(j) && all(j>=1) && all(j<=N)) || usage("j must be an integer or vector with elements in 1..%d", N);
+    j = j(:)'; # make j a row vector
+    b = ones(N,1);
+    A = -P;
+    A(:,j) = 0;
+    A += eye(N,N);
+    res = A \ b;
+    result = res(i);
+  endif
+endfunction
+%!demo
+%! P = [ 0.0 0.9 0.1; \
+%!       0.1 0.0 0.9; \
+%!       0.9 0.1 0.0 ];
+%! M = dtmc_fpt(P);
+
+%!test
+%! P = [ 0.0 0.9 0.1; \
+%!       0.1 0.0 0.9; \
+%!       0.9 0.1 0.0 ];
+%! p = dtmc(P);
+%! M = dtmc_fpt(P);
+%! assert( diag(M)', 1./p, 1e-8 );
+
+%!test
+%! P = [ 0.0 0.9 0.1; \
+%!       0.1 0.0 0.9; \
+%!       0.9 0.1 0.0 ];
+%! p = dtmc(P);
+%! m = dtmc_fpt(P, 1, 1);
+%! assert( m, 1/p(1), 1e-8 );
+
+%!test
+%! P = [ 0.0 0.9 0.1; \
+%!       0.1 0.0 0.9; \
+%!       0.9 0.1 0.0 ];
+%! m = dtmc_fpt(P, 1, [2 3]);
+
+%!test
+%! P = unifrnd(0.1,0.9,10,10);
+%! normP = repmat(sum(P,2),1,columns(P));
+%! P = P./normP;
+%! M = dtmc_fpt(P);
+%! for i=1:rows(P)
+%!   for j=1:columns(P)
+%!     assert( M(i,j), 1 + dot(P(i,:), M(:,j)) - P(i,j)*M(j,j), 1e-8);
+%!     assert( M(i,j), dtmc_fpt(P, i, j), 1e-8 );
+%!   endfor
+%! endfor
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/dtmc_solve.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,35 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{p} =} dtmc_solve (@var{P})
+##
+## This function is deprecated and will be removed from future
+## versions of @code{queueing}. Please use @code{dtmc()} instead.
+##
+## @seealso{dtmc}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function q = dtmc_solve( P )
+  warning("Function dtmc_solve() is deprecated and will be removed in the future. Please use dtmc() instead.");
+  q = dtmc(P);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/population_mix.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,141 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {pop_mix =} population_mix (@var{k}, @var{N})
+##
+## @cindex population mix
+## @cindex closed network, multiple classes
+##
+## Return the set of valid population mixes with exactly @var{k}
+## customers, for a closed multiclass Queueing Network with population
+## vector @var{N}. More specifically, given a multiclass Queueing
+## Network with @math{C} customer classes, such that there are
+## @code{@var{N}(i)} requests of class @math{i}, a
+## @math{k}-mix @var{mix} is a @math{C}-dimensional vector with the
+## following properties:
+##
+## @example
+## @group
+## all( mix >= 0 );
+## all( mix <= N );
+## sum( mix ) == k;
+## @end group
+## @end example
+## 
+## @noindent This function enumerates all valid @math{k}-mixes, such that
+## @code{@var{pop_mix}(i)} is a @math{C} dimensional row vector representing
+## a valid population mix, for all @math{i}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item k
+## Total population size of the requested mix. @var{k} must be a nonnegative integer
+##
+## @item N
+## @code{@var{N}(i)} is the number of class @math{i} requests.
+## The condition @code{@var{k} @leq{} sum(@var{N})} must hold.
+## 
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item pop_mix
+## @code{@var{pop_mix}(i,j)} is the number of class @math{j} requests
+## in the @math{i}-th population mix. The number of
+## population mixes is @code{rows( @var{pop_mix} ) }.
+##
+## @end table
+##
+## Note that if you are interested in the number of @math{k}-mixes
+## and you don't care to enumerate them, you can use the funcion
+## @code{qnmvapop}.
+##
+## @seealso{qnmvapop}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function pop_mix = population_mix( k, population )
+
+  if ( nargin != 2 ) 
+    print_usage();
+  endif
+
+  isvector( population ) && all( population>=0 ) || \
+      usage( "N must be an array >=0" );
+  R = length(population); # number of classes
+  ( isscalar(k) && k >= 0 && k <= sum(population) ) || \
+      usage( "k must be a scalar <= %d", sum(population));
+  N = zeros(1, R);
+  const = min(k, population);
+  mp = 0;
+  pop_mix = []; # Init result
+  while ( N(R) <= const(R) )
+    x=k-mp;
+    ## Fill the current configuration
+    i=1;
+    while ( x>0 && i<=R )
+      N(i) = min(x,const(i));
+      x = x-N(i);
+      mp = mp+N(i);
+      i = i+1;
+    endwhile
+
+    ## here the configuration is filled. add it to the set of mixes
+    assert( sum(N), k );
+    pop_mix = [pop_mix; N]; ## FIXME: pop_mix is continuously resized
+
+    ## advance to the next feasible configuration
+    i = 1;
+    sw = true;
+    while sw
+      if ( ( mp==k || N(i)==const(i)) && ( i<R ) )
+        mp = mp-N(i);
+        N(i) = 0;
+        i=i+1;
+      else
+        N(i)=N(i)+1;
+        mp=mp+1;
+        sw = false;
+      endif
+    endwhile
+  endwhile
+endfunction
+%!test
+%! N = [2 3 4];
+%! f = population_mix( 1, N );
+%! assert( f, [1 0 0; 0 1 0; 0 0 1] );
+%! f = population_mix( 2, N );
+%! assert( f, [2 0 0; 1 1 0; 0 2 0; 1 0 1; 0 1 1; 0 0 2] );
+%! f = population_mix( 3, N );
+%! assert( f, [2 1 0; 1 2 0; 0 3 0; 2 0 1; 1 1 1; 0 2 1; 1 0 2; 0 1 2; 0 0 3] );
+
+%!test
+%! N = [2 1];
+%! f = population_mix( 1, N );
+%! assert( f, [1 0; 0 1] );
+%! f = population_mix( 2, N );
+%! assert( f,  [2 0; 1 1] );
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnammm.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,102 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnammm (@var{lambda}, @var{mu})
+##
+## @cindex Asymmetric @math{M/M/m} system
+##
+## Compute @emph{approximate} utilization, response time, average number
+## of requests in service and throughput for an asymmetric  @math{M/M/m}
+## queue. In this system there are @math{m} different service centers
+## connected to a single queue. Each server has its own (possibly different)
+## service rate. If there is more than one server available, requests
+## are routed to a randomly-chosen one.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate (@code{@var{lambda}>0}).
+##
+## @item mu
+## @code{@var{mu}(i)} is the service rate of server
+## @math{i}, @math{1 @leq{} i @leq{} m}.
+## The system must be ergodic (@code{@var{lambda} < sum(@var{mu})}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## Approximate service center utilization,
+## @math{U = \lambda / ( \sum_i \mu_i )}.
+##
+## @item R
+## Approximate service center response time
+##
+## @item Q
+## Approximate number of requests in the system
+##
+## @item X
+## Approximate service center throughput. If the system is ergodic, 
+## we will always have @code{@var{X} = @var{lambda}}
+##
+## @end table
+##
+## @seealso{qnmmm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pm] = qnammm( lambda, mu )
+  if ( nargin != 2 )
+    print_usage();
+  endif
+
+  ( isscalar(lambda) && isvector(mu) ) || \
+      usage( "the parameters must be vectors" );
+
+  m = length(mu); # number of servers
+
+  all( lambda < sum(mu) ) || \
+      error( "Processing capacity exceeded" );
+
+  X = lambda;
+  U = rho = lambda / sum(mu);
+  Q = p0 = 0;
+  k=[0:m-1];
+  p0 = 1 / ( ...
+            sum( (m*rho).^k ./ factorial(k)) + ...
+            (m*rho)^m / (factorial(m)*(1-rho)) ...
+            );
+  pm = (m*rho)^m/(factorial(m)*(1-rho))*p0;
+  Q = m*rho+rho / (1-rho) * pm;
+  R = Q / X;
+endfunction
+%!test
+%! [U R Q X] = qnammm( 73,[10,15,20,20,25] );
+%! assert( U, 0.81, 1e-2 );
+%! assert( Q, 6.5278, 1e-4 );
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosed.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,101 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosed (@var{N}, @var{S}, @var{V}, @dots{})
+##
+## @cindex closed network
+##
+## This function computes steady-state performance measures of closed
+## queueing networks using the Mean Value Analysis (MVA) algorithm. The
+## qneneing network is allowed to contain fixed-capacity centers, delay
+## centers or general load-dependent centers. Multiple request
+## classes are supported.
+##
+## This function dispatches the computation to one of
+## @code{qnclosedsinglemva}, @code{qnclosedsinglemvald} or
+## @code{qnclosedmultimva}.
+##
+## @itemize
+##
+## @item If @var{N} is a scalar, the network is assumed to have a single
+## class of requests; in this case, the exact MVA algorithm is used to
+## analyze the network. If @var{S} is a vector, then @code{@var{S}(k)}
+## is the average service time of center @math{k}, and this function
+## calls @code{qnclosedsinglemva} which supports load-independent
+## service centers. If @var{S} is a matrix, @code{@var{S}(k,i)} is the
+## average service time at service center @math{k} when @math{i @geq{}
+## 1} jobs are present; in this case, the network is analyzed with the
+## @code{qnclosedsinglemvald} function.
+##
+## @item If @var{N} is a vector, the network is assumed to have multiple
+## classes of requests, and is analyzed using the exact multiclass
+## MVA algorithm as implemented in the @code{qnclosedmultimva} function.
+##
+## @end itemize
+##
+## @seealso{qnclosedsinglemva, qnclosedsinglemvald, qnclosedmultimva}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnclosed( N, S, V, varargin )
+  if ( nargin < 3 )
+    print_usage();
+  endif
+  if ( isscalar(N) )
+    if ( isvector(S) ) 
+      [U R Q X] = qnclosedsinglemva( N, S, V, varargin{:} );
+    else
+      [U R Q X] = qnclosedsinglemvald( N, S, V, varargin{:} );
+    endif
+  else
+    [U R Q X] = qnclosedmultimva( N, S, V, varargin{:} );
+  endif
+endfunction
+
+%!demo
+%! P = [0 0.3 0.7; 1 0 0; 1 0 0]; # Transition probability matrix
+%! S = [1 0.6 0.2]; # Average service times
+%! m = ones(1,3); # All centers are single-server
+%! Z = 2; # External delay
+%! N = 15; # Maximum population to consider
+%!
+%! V = qnvisits(P); # Compute number of visits from P
+%! D = V .* S; # Compute service demand from S and V
+%! X_bsb_lower = X_bsb_upper = zeros(1,N);
+%! X_ab_lower = X_ab_upper = zeros(1,N);
+%! X_mva = zeros(1,N);
+%! for n=1:N
+%!   [X_bsb_lower(n) X_bsb_upper(n)] = qnclosedbsb(n, D, Z);
+%!   [X_ab_lower(n) X_ab_upper(n)] = qnclosedab(n, D, Z);
+%!   [U R Q X] = qnclosed( n, S, V, m, Z );
+%!   X_mva(n) = X(1)/V(1);
+%! endfor
+%! close all;
+%! plot(1:N, X_ab_lower,"g;Asymptotic Bounds;", \
+%!      1:N, X_bsb_lower,"k;Balanced System Bounds;", \
+%!      1:N, X_mva,"b;MVA;", "linewidth", 2, \
+%!      1:N, X_bsb_upper,"k", \
+%!      1:N, X_ab_upper,"g" );
+%! axis([1,N,0,1]);
+%! xlabel("Number of Requests n");
+%! ylabel("System Throughput X(n)");
+%! legend("location","southeast");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosedab.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,102 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnclosedab (@var{N}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnclosedab (@var{N}, @var{D}, @var{Z})
+##
+## @cindex bounds, asymptotic
+## @cindex closed network
+##
+## Compute Asymptotic Bounds for single-class, closed Queueing Networks
+## with @math{K} service centers.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## number of requests in the system (scalar, @code{@var{N}>0}).
+##
+## @item D
+## @code{@var{D}(k)} is the service demand of service center @math{k},
+## @code{@var{D}(k) @geq{} 0}.
+##
+## @item Z
+## external delay (think time, scalar, @code{@var{Z} @geq{} 0}). If
+## omitted, it is assumed to be zero.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @itemx Xu
+## Lower and upper bound on the system throughput.
+##
+## @item Rl
+## @itemx Ru
+## Lower and upper bound on the system response time.
+##
+## @end table
+##
+## @seealso{qnclosedbsb, qnclosedgb, qnclosedpb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xl Xu Rl Ru] = qnclosedab( N, D, Z )
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+  ( isscalar(N) && N > 0 ) || \
+      usage( "N must be a positive integer" );
+  ( isvector(D) && length(D)>0 && all( D >= 0 ) ) || \
+      usage( "D must be a vector of nonnegative floats" );
+  if ( nargin < 3 )
+    Z = 0;
+  else
+    ( isscalar(Z) && Z >= 0 ) || \
+        usage( "Z must be a nonnegative scalar" );
+  endif
+  
+  D_tot = sum(D);
+  D_max = max(D);
+  Xl = N/(N*D_tot+Z);
+  Xu = min( N/(D_tot+Z), 1/D_max );
+  Rl = max( D_tot, N*D_max-Z );
+  Ru = N*D_tot;
+endfunction
+
+%!test
+%! fail( "qnclosedab( 1, [] )", "vector" );
+%! fail( "qnclosedab( 1, [0 -1])", "vector" );
+%! fail( "qnclosedab( 0, [1 2] )", "positive integer" );
+%! fail( "qnclosedab( -1, [1 2])", "positive integer" );
+
+## Example 9.6 p. 913 Bolch et al.
+%!test
+%! N = 20;
+%! D = [ 4.6*2 8 ];
+%! Z = 120;
+%! [X_l X_u R_l R_u] = qnclosedab(N, D, Z);
+%! assert( [X_u R_l], [0.109 64], 1e-3 );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosedbsb.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,96 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnclosedbsb (@var{N}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnclosedbsb (@var{N}, @var{D}, @var{Z})
+##
+## @cindex bounds, balanced system
+## @cindex closed network
+##
+## Compute Balanced System Bounds for single-class, closed Queueing Networks
+## with @math{K} service centers.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## number of requests in the system (scalar).
+##
+## @item D
+## @code{@var{D}(k)} is the service demand at center @math{k};
+## @code{@var{K}(k) @geq{} 0}.
+##
+## @item Z
+## external delay (think time, scalar, @code{@var{Z} @geq{} 0}). If
+## omitted, it is assumed to be zero.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @itemx Xu
+## Lower and upper bound on the system throughput.
+##
+## @item Rl
+## @itemx Ru
+## Lower and upper bound on the system response time.
+##
+## @end table
+##
+## @seealso{qnclosedab, qnclosedgb, qnclosedpb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xl Xu Rl Ru] = qnclosedbsb( N, D, Z )
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+  ( isscalar(N) && N>0 ) || \
+      usage( "N must be a positive scalar" );
+  ( isvector(D) && length(D)>0 && all(D>=0) ) || \
+      usage( "D must be a vector of nonnegative floats" );
+  if ( nargin < 3 )
+    Z = 0;
+  else
+    ( isscalar(Z) && Z>=0 ) || \
+        usage( "Z must be a nonnegative scalar" );
+  endif
+
+  D_max = max(D);
+  D_tot = sum(D);
+  D_ave = mean(D);
+  Xl = N/(D_tot+Z+( (N-1)*D_max )/( 1+Z/(N*D_tot) ) );
+  Xu = min( 1/D_max, N/( D_tot+Z+( (N-1)*D_ave )/(1+Z/D_tot) ) );
+  Rl = max( N*D_max-Z, D_tot+( (N-1)*D_ave )/( 1+Z/D_tot) );
+  Ru = D_tot + ( (N-1)*D_max )/( 1+Z/(N*D_tot) );
+endfunction
+
+%!test
+%! fail("qnclosedbsb(1)");
+%! fail("qnclosedbsb(1, [])", "vector");
+%! fail("qnclosedbsb(-1,[1 1 1], [1 1 1])", "positive scalar");
+%! fail("qnclosedbsb(1,[-1 0 0], [1 1 1])", "nonnegative");
+%! fail("qnclosedbsb(1,[0 0 0],-1)", "nonnegative scalar");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosedgb.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,214 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Ql}, @var{Qu}] =} qnclosedgb (@var{N}, @var{D}, @var{Z})
+##
+## @cindex bounds, geometric
+## @cindex closed network
+##
+## Compute Geometric Bounds (GB) for single-class, closed Queueing Networks.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## number of requests in the system (scalar, @code{@var{N} > 0}).
+##
+## @item D
+## @code{@var{D}(k)} is the service demand of service center @math{k}
+## (@code{@var{D}(k) @geq{} 0}).
+##
+## @item Z
+## external delay (think time, scalar). If omitted, it is assumed to be zero.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @itemx Xu
+## Lower and upper bound on the system throughput. If @code{@var{Z}>0},
+## these bounds are computed using @emph{Geometric Square-root Bounds}
+## (GSB). If @code{@var{Z}==0}, these bounds are computed using @emph{Geometric Bounds} (GB)
+##
+## @item Ql
+## @itemx Qu
+## @code{@var{Ql}(i)} and @code{@var{Qu}(i)} are the lower and upper
+## bounds respectively of the queue length for service center @math{i}.
+##
+## @end table
+##
+## @seealso{qnclosedab}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_lower X_upper Q_lower Q_upper] = qnclosedgb( N, L, Z, X_minus, X_plus )
+  ## The original paper uses the symbol "L" instead of "D" to denote the
+  ## loadings of service centers. In this function we adopt the same
+  ## notation as the paper.
+  if ( nargin < 2 || nargin > 5 )
+    print_usage();
+  endif
+  ( isscalar(N) && N > 0 ) || \
+      usage( "N must be >0" );
+  ( isvector(L) && length(L) > 0 && all( L >= 0 ) ) || \
+      usage( "D must be a vector >=0" );
+  L = L(:)'; # make L a row vector
+  if ( nargin < 3 )
+    Z = 0;
+  else
+    ( isscalar(Z) && (Z >= 0) ) || \
+        usage( "Z must be >=0" );
+  endif
+  L_tot = sum(L);
+  L_max = max(L);
+  M = length(L);
+  if ( nargin < 4 ) 
+    [X_minus X_plus] = qnclosedab(N,L,Z);
+  endif
+  ##[X_minus X_plus] = [0 1/L_max];
+  [Q_lower Q_upper] = __compute_Q( N, L, Z, X_plus, X_minus);
+  [Q_lower_Nm1 Q_upper_Nm1] = __compute_Q( N-1, L, Z, X_plus, X_minus);
+  if ( Z > 0 )
+    ## Use Geometric Square-root Bounds (GSB)
+    i = find(L<L_max);
+    bN = Z+L_tot+L_max*(N-1)-sum( (L_max-L(i)).*Q_lower_Nm1(i) );
+    X_lower = 2*N/(bN+sqrt(bN^2-4*Z*L_max*(N-1)));
+    bN = Z+L_tot+L_max*(N-1)-sum( (L_max-L(i)).*Q_upper_Nm1(i) );
+    X_upper = 2*N/(bN+sqrt(bN^2-4*Z*L_max*N));
+  else
+    ## Use Geometric Bounds (GB). FIXME: given that this branch is
+    ## executed when Z=0, the expressions below can be simplified.
+    X_lower = N/(Z+L_tot+L_max*(N-1-Z*X_minus) - ...
+                 sum( (L_max - L) .* Q_lower_Nm1 ) );
+    X_upper = N/(Z+L_tot+L_max*(N-1-Z*X_plus) - ...
+                 sum( (L_max - L) .* Q_upper_Nm1 ) );
+  endif
+endfunction
+
+## [ Q_lower Q_uppwer ] = __compute_Q( N, D, Z, X_plus, X_minus )
+##
+## compute Q_lower(i) and Q_upper(i), the lower and upper bounds
+## respectively for queue length at service center i, for a closed
+## network with N customers, service demands D and think time Z. This
+## function uses Eq. (8) and (13) from the reference paper.
+function [ Q_lower Q_upper ] = __compute_Q( N, L, Z, X_plus, X_minus )
+  isscalar(X_plus) || usage( "X_plus must be a scalar" );
+  isscalar(X_minus) || usage( "X_minus must be a scalar" );
+  ( isscalar(N) && (N>=0) ) || usage( "N is not valid" );
+  L_tot = sum(L);
+  L_max = max(L);
+  M = length(L);
+  m_max = sum( L == L_max );
+  y = Y = zeros(1,M);
+  ## first, handle the case of servers with loading less than the
+  ## maximum that is, L(i) < L_max
+  i=find(L<L_max);
+  y(i) = L(i)*N./(Z+L_tot+L_max*N);
+  Q_lower(i) = y(i)./(1-y(i)) .- (y(i).^(N+1))./(1-y(i)); # Eq. (8)
+  Y(i) = L(i)*X_plus;
+  Q_upper(i) = Y(i)./(1-Y(i)) .- (Y(i).^(N+1))./(1-Y(i)); # Eq. (13)
+  ## now, handle the case of servers with demand equal to the maximum
+  i=find(L==L_max);
+  Q_lower(i) = 1/m_max*(N-Z*X_plus - sum( Q_upper( find(L<L_max) ) ) ); \
+				# Eq. (8)
+      Q_upper(i) = 1/m_max*(N-Z*X_minus - sum( Q_lower( find(L<L_max) \
+						       ) ) ); # Eq. (13)
+endfunction
+
+%!test
+%! fail( "qnclosedpb( 1, [] )", "vector" );
+%! fail( "qnclosedpb( 1, [0 -1])", "vector" );
+%! fail( "qnclosedpb( 0, [1 2] )", "positive integer" );
+%! fail( "qnclosedpb( -1, [1 2])", "positive integer" );
+
+%!# shared test function
+%!function test_gb( D, expected, Z=0 )
+%! for i=1:rows(expected)
+%!   N = expected(i,1);
+%!   [X_lower X_upper Q_lower Q_upper] = qnclosedgb(N,D,Z);
+%!   X_exp_lower = expected(i,2);
+%!   X_exp_upper = expected(i,3);
+%!   assert( [N X_lower X_upper], [N X_exp_lower X_exp_upper], 1e-4 )
+%! endfor
+
+%!xtest
+%! # table IV
+%! D = [ 0.1 0.1 0.09 0.08 ];
+%! #            N  X_lower  X_upper
+%! expected = [ 2  4.3040   4.3174; ...
+%!              5  6.6859   6.7524; ...
+%!              10 8.1521   8.2690; ...
+%!              20 9.0947   9.2431; ...
+%!              80 9.8233   9.8765 ];
+%! test_gb(D, expected);
+
+%!xtest
+%! # table V
+%! D = [ 0.1 0.1 0.09 0.08 ];
+%! Z = 1;
+%! #            N  X_lower  X_upper
+%! expected = [ 2  1.4319   1.5195; ...
+%!              5  3.3432   3.5582; ...
+%!              10 5.7569   6.1410; ...
+%!              20 8.0856   8.6467; ...
+%!              80 9.7147   9.8594];
+%! test_gb(D, expected, Z);
+
+%!test
+%! P = [0 0.3 0.7; 1 0 0; 1 0 0];
+%! S = [1 0.6 0.2];
+%! m = ones(1,3);
+%! V = qnvisits(P);
+%! Nmax = 20;
+%!
+%! ## Test case with Z>0
+%! for n=1:Nmax
+%!   [X_gb_lower X_gb_upper Q_gb_lower Q_gb_upper] = qnclosedgb(n, S.*V, 2);
+%!   [U R Q X] = qnclosed( n, S, V, m, 2 );
+%!   X_mva = X(1)/V(1);
+%!   assert( X_gb_lower <= X_mva );
+%!   assert( X_gb_upper >= X_mva );
+%!   assert( Q_gb_lower <= Q+1e-5 ); # compensate for numerical errors
+%!   assert( Q_gb_upper >= Q-1e-5 ); # compensate for numerical errors
+%! endfor
+
+%!test
+%! P = [0 0.3 0.7; 1 0 0; 1 0 0];
+%! S = [1 0.6 0.2];
+%! m = ones(1,3);
+%! V = qnvisits(P);
+%! Nmax = 20;
+%!
+%! ## Test case with Z=0
+%! for n=1:Nmax
+%!   [X_gb_lower X_gb_upper Q_gb_lower Q_gb_upper] = qnclosedgb(n, S.*V, 0);
+%!   [U R Q X] = qnclosed( n, S, V, m, 0 );
+%!   X_mva = X(1)/V(1);
+%!   assert( X_gb_lower <= X_mva );
+%!   assert( X_gb_upper >= X_mva );
+%!   assert( Q_gb_lower <= Q );
+%!   assert( Q_gb_upper >= Q );
+%! endfor
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosedmultimva.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,760 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimva (@var{N}, @var{S} )
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimva (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimva (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimva (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimva (@var{N}, @var{S}, @var{P})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimva (@var{N}, @var{S}, @var{P}, @var{m})
+##
+## @cindex Mean Value Analysys (MVA)
+## @cindex closed network, multiple classes
+##
+## Analyze closed, multiclass queueing networks with @math{K} service
+## centers and @math{C} independent customer classes (chains) using the
+## Mean Value Analysys (MVA) algorithm. 
+##
+## Queueing policies at service centers can be any of the following:
+##
+## @table @strong
+##
+## @item FCFS
+## (First-Come-First-Served) customers are served in order of arrival;
+## multiple servers are allowed. For this kind of queueing discipline,
+## average service times must be class-independent.
+##
+## @item PS
+## (Processor Sharing) customers are served in parallel by a single
+## server, each customer receiving an equal share of the service rate.
+##
+## @item LCFS-PR
+## (Last-Come-First-Served, Preemptive Resume) customers are served in
+## reverse order of arrival by a single server and the last arrival
+## preempts the customer in service who will later resume service at the
+## point of interruption.
+##
+## @item IS
+## (Infinite Server) customers are delayed independently of other
+## customers at the service center (there is effectively an infinite
+## number of servers).
+##
+## @end table
+##
+## @quotation Note
+## If this function is called specifying the visit ratios
+## @var{V}, class switching is @strong{not} allowed.
+##
+## If this function is called specifying the routing probability matrix
+## @var{P}, then class switching @strong{is} allowed; however, in this
+## case all nodes are restricted to be fixed rate service centers or
+## delay centers: multiple-server and general load-dependent
+## centers are not supported. @end quotation
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## @code{@var{N}(c)} is the number of class @math{c} requests in the
+## system; @code{@var{N}(c) @geq{} 0}. If class @math{c} has
+## no requests (@code{@var{N}(c) = 0}), then
+## @code{@var{U}(c,k) = @var{R}(c,k) = @var{Q}(c,k) = @var{X}(c,k) = 0}
+## for all @var{k}.
+##
+## @item S
+## @code{@var{S}(c,k)} is the mean service time for class @math{c}
+## customers at center @math{k} (@code{@var{S}(c,k) @geq{} 0}).
+## If service time at center @math{k} is class-dependent,
+## then center #math{k} is assumed to be of type @math{-/G/1}--PS
+## (Processor Sharing).
+## If center @math{k} is a FCFS node (@code{@var{m}(k)>1}), then the
+## service times @strong{must} be class-independent.
+##
+## @item V
+## @code{@var{V}(c,k)} is the average number of visits of class @math{c}
+## customers to service center @math{k}; @code{@var{V}(c,k) @geq{} 0},
+## default is 1.
+## @strong{If you pass this parameter, no class switching is not
+## allowed}
+##
+## @item P
+## @code{@var{P}(r,i,s,j)} is the probability that a class @math{r}
+## job completing service at center @math{i} is routed to center @math{j}
+## as a class @math{s} job. @strong{If you pass this parameter, 
+## class switching is allowed}.
+##
+## @item m
+## If @code{@var{m}(k)<1}, then center @math{k} is assumed to be a delay
+## center (IS node @math{-/G/\infty}). If @code{@var{m}(k)==1}, then
+## service center @math{k} is a regular queueing center
+## (@math{M/M/1}--FCFS, @math{-/G/1}--LCFS-PR or @math{-/G/1}--PS).
+## Finally, if @code{@var{m}(k)>1}, center @math{k} is a
+## @math{M/M/m}--FCFS center with @code{@var{m}(k)} identical servers.
+## Default is @code{@var{m}(k)=1} for each @math{k}.
+##
+## @item Z
+## @code{@var{Z}(c)} is the class @math{c} external delay (think time);
+## @code{@var{Z}(c) @geq{} 0}. Default is 0.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## If @math{k} is a FCFS, LCFS-PR or PS node, then @code{@var{U}(c,k)}
+## is the class @math{c} utilization at center
+## @math{k}. If @math{k} is an IS node, then @code{@var{U}(c,k)} is the
+## class @math{c} @emph{traffic intensity} at center @math{k},
+## defined as @code{@var{U}(c,k) = @var{X}(c,k)*@var{S}(c,k)}. 
+##
+## @item R
+## @code{@var{R}(c,k)} is the class @math{c} response time at
+## center @math{k}. The total class @math{c} system response time
+## can be computed as @code{dot(@var{R}, @var{V}, 2)}.
+##
+## @item Q
+## @code{@var{Q}(c,k)} is the average number of
+## class @math{c} requests at center @math{k}. The total number of
+## requests at center @math{k} is @code{sum(@var{Q}(:,k))}. 
+## The total number of class @math{c} requests in the system
+## is @code{sum(@var{Q}(c,:))}.
+##
+## @item X
+## @code{@var{X}(c,k)} is the class @math{c} throughput at
+## center @math{k}. The class @math{c} system throughput can be computed
+## as @code{@var{X}(c,1) / @var{V}(c,1)}.
+##
+## @end table
+##
+## @seealso{qnclosed, qnclosedmultimvaapprox}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnclosedmultimva( N, S, V, varargin )
+
+  if ( nargin < 2 || nargin > 5 )
+    print_usage();
+  endif
+
+  if ( nargin == 2 )
+    V = ones(size(S));
+  endif
+
+  ( ismatrix(V) && (ndims(V) == 2 || ndims(V) == 4) ) || \
+      usage("The third parameter has %d dimensions (must be 2- or 4-dimensional)", ndims(V) );
+
+  if ( ndims(V) == 2 )
+    [U R Q X] = __qnclosedmultimva_nocs( N, S, V, varargin{:} );
+  else
+    [U R Q X] = __qnclosedmultimva_cs( N, S, V, varargin{:} );
+  endif
+
+endfunction
+
+##############################################################################
+## Analyze closed, multiclass QNs with class switching
+function [U R Q X] = __qnclosedmultimva_cs( N, S, P, m )
+  
+  if ( nargin < 3 || nargin > 4 )
+    print_usage();
+  endif
+
+  isvector(N) && all( N>=0 ) || \
+      usage( "N must be >=0" );
+  N = N(:)'; # make N a row vector
+  C = length(N); ## Number of classes
+  K = columns(S); ## Number of service centers
+  ( ndims(S) == 2 ) || \
+      usage( "S must be a %dx%d matrix", C, K );
+  size(S) == [C,K] || \
+      usage( "S size mismatch (is %dx%d, should be %dx%d)", rows(S), columns(S), C, K );
+  ndims(P) == 4 && size(P) == [C,K,C,K] || \
+      usage( "P size mismatch" );
+
+  if ( nargin < 4 ) 
+    m = ones(1,K);
+  else
+    isvector(m) || \
+        usage( "m must be a vector" );
+    m = m(:)'; # make m a row vector
+    length(m) == K || \
+        usage( "m size mismatch (should be %d, is %d)", K, length(m) );
+  endif
+
+  ## Check consistency of parameters
+  all( all( S >= 0 ) ) || \
+      usage( "S must be >0" );
+  all( any(S>0,2) ) || \
+      usage( "S must contain at least a value >0 for each row" );
+  all( all( P >= 0 ) ) || \
+      usage( "V must be >=0" );
+
+  U = R = Q = X = zeros(C,K);
+
+  ## 1. Compute visit counts
+  [V ch] = qnvisits(P);
+
+  ## 2. Identify chains
+  ## ch = qnchains(P);
+  nch = max(ch);
+  
+  ## 3. Compute visit counts for the equivalent network
+  Vstar = zeros(nch,K);
+  for q=1:nch
+    r = (ch == q);
+    Vstar(q,:) = sum(V(r,:),1);
+  endfor
+
+  ## 4. Compute proportionality constants
+  alpha = zeros(C,K);
+  for r=1:C
+    for k=find( Vstar(ch(r),:) > 0 ) 
+      alpha(r,k) = V(r,k) / Vstar( ch(r), k );
+    endfor
+  endfor
+
+  ## 5. Compute service times
+  Sstar = zeros(nch,K);
+  for q=1:nch
+    r = (ch==q);
+    Sstar(q,:) = dot( alpha(r,:), S(r,:), 1 );
+  endfor
+
+  ## 6. Compute populations of superclasses
+  Nstar = zeros(1,nch);
+  for q=1:nch
+    r = (ch == q);
+    Nstar(q) = sum( N(r) );
+  endfor
+
+  ## 7. Solve the equivalent network
+  [Ustar Rstar Qstar Xstar Qnm1] = __qnclosedmultimva_nocs( Nstar, Sstar, Vstar, m );
+
+  ## 8. Compute solutions of the original network
+  for r=1:C
+    for k=1:K
+      R(r,k) = S(r,k) * (1 + Qnm1(ch(r),k)*(m(k)==1));
+      X(r,k) = alpha(r,k) * Xstar(ch(r),k);
+      Q(r,k) = X(r,k) * R(r,k);
+      U(r,k) = S(r,k) * X(r,k);
+    endfor
+  endfor
+
+endfunction
+
+##############################################################################
+## Analyze closed, multiclass QNs WITHOUT class switching
+##
+## This implementation is based on:
+##
+## Herb Schwetman, "Implementing the MEan Value Algorithm for the
+## Solution of Queueing Network Models", technical report OSD-TR-355,
+## dept. of Computer Science, Purdue University, feb. 1982.
+##
+function [U R Q X Qnm1] = __qnclosedmultimva_nocs( N, S, V, m, Z )
+
+  if ( nargin < 3 || nargin > 5 )
+    print_usage();
+  endif
+
+  isvector(N) && all( N>=0 ) || \
+      usage( "N must be >=0" );
+  N = N(:)'; # make N a row vector
+  C = length(N); ## Number of classes
+  K = columns(S); ## Number of service centers
+  size(S) == [C,K] || \
+      usage( "S size mismatch" );
+  size(V) == [C,K] || \
+      usage( "V size mismatch" );
+
+  if ( nargin < 4 ) 
+    m = ones(1,K);
+  else
+    isvector(m) || \
+        usage( "m must be a vector" );
+    m = m(:)'; # make m a row vector
+    length(m) == K || \
+        usage( "m size mismatch (should be %d, is %d)", K, length(m) );
+  endif
+
+  if ( nargin < 5 )
+    Z = zeros(1,C);
+  else
+    isvector(Z) || \
+        usage( "Z must be a vector" );
+    Z = Z(:)'; # make Z a row vector
+    length(Z) == C || \
+	usage( "Z size mismatch (should be %d, is %d)", C, length(Z) );
+  endif
+
+  ## Check consistency of parameters
+  all( all( S >= 0 ) ) || \
+      usage( "S must be >=0" );
+  all( any(S>0,2) ) || \
+      usage( "S must contain at least a value >0 for each row" );
+  all( all( V >= 0 ) ) || \
+      usage( "V must be >=0" );
+
+  ## ensure that the service times for multiserver nodes
+  ## are class-independent
+  for k=find(m>1)
+    all( S(:,k) == S(1,k) ) || \
+        error( "Service times for FCFS node %d are not class-independent", k );
+  endfor
+
+  ## Initialize results
+  R = zeros( C, K );
+  X = zeros( 1, C );
+  D = S .* V;
+
+  ## The multiclass MVA algorithm requires to store the queue lengths Q(
+  ## _n_, k ) at center k where the population vector is _n_. The space
+  ## required would be K*prod(N+1), but this can be reduced by
+  ## considering that, at each iteration of the main MVA loop, the total
+  ## number of requests is n; therefore it is sufficient to consider the
+  ## first (C-1) components of vector _n_ to uniquely identify the cell
+  ## containing Q( _n_, k ). See Schwetman for a better explanation.
+  bufsize = prod((N+1)(1:end-1));
+  Q_next = Q = zeros( bufsize,K );
+
+  p = cell(1,K);
+  for k=find(m>1)
+    ## p{i}(j+1,k+1) is the probability to have j jobs at node i
+    ## where the network is in state k
+    p{k} = zeros( m(k)+1,bufsize );
+    p{k}(1,__getidx(N,0*N)) = 1;
+  endfor
+
+  Qnm1= zeros(C,K); ## Qnm1(c,k) is the number of requests in center k, provided that the population size is N-1_c (N is the total population vector). This value is needed by __qnclosedmultimva_cs. Qnm1 is only filled for M/M/1 or PS centers. The values are not computed for PS nodes
+
+  dd = zeros(1,C);
+  for c=find(N>0)
+    h = zeros(1,C); h(c) = 1;
+    dd(c) = __getidx(N,h)-1;
+  endfor
+
+  for n=1:sum(N)
+
+    ## MVA iteration for population size n
+    n_bar = zeros(1, C);
+    const = min(n, N);
+    mp = 0;
+    while ( n_bar(C) <= const(C) )
+      
+      ## Fill the current configuration (algorithm 3b, p. 10, Schwetman)
+      x=n-mp;
+      i=1;
+      while ( x>0 && i<=C )
+	n_bar(i) = min(x,const(i));
+	x -= n_bar(i);
+	mp += n_bar(i);
+	i += 1;
+      endwhile
+
+      idx = __getidx( N, n_bar );
+      
+      R = S;
+
+      ## Compute response time for LI servers
+      k=find(m==1);
+      for c=find(n_bar>0)
+	## idx-dd(c) is the index of element n_bar - 1_c
+        R(c,k) = S(c,k).*(1 + Q( idx-dd(c), k ) );  
+        Qnm1(c,k) = Q( idx-dd(c), k);
+        ## for FCFS nodes with class-dependent service times,
+        ## it is possible to use the following approximation
+        ## (p. 469 Bolch et al.)
+        ##
+        ## R(c,k) = S(c,k) + sum( S(:,k) * Q(idx(:), k) );
+	## R(c,k) = S(c,k) + sum( S(:,k) .* Q(idx, k) .* V(:,k) ) / sum(V(:,k));
+      endfor
+
+      ## Compute response time for LD servers
+      for k=find(m>1)
+        j=0:m(k)-2; # range
+	for c=find(n_bar > 0 )
+          R(c,k) = S(c,k)/m(k)*(1 + Q( idx-dd(c), k ) + ...
+                                dot(m(k)-j-1,p{k}(j+1,idx-dd(c)) ) );
+	endfor
+      endfor
+
+      X = n_bar ./ ( Z .+ dot(R,V,2)' ); # X(c) = N(c) / ( Z(c) + sum_k R(c,k) * V(c,k) )
+
+      ## Q_k = sum_c X(c) * R(c,k) * V(c,k)
+      Q_next( idx, : ) = (X * (R .* V))';
+      ## Q( idx, : ) = (X * (R .* V))';
+
+      ## Update marginal probabilities for LD servers
+      for k=find(m>1)
+        s=0; # s is actually a vector
+        j=1:m(k)-1;
+        for r=find(n_bar>0) # FIXME: I don't know how to vectorize this
+          s+=D(r,k)*X(r)*p{k}(j,idx-dd(r));
+        endfor
+        p{k}(j+1,idx) = s./j;
+        p{k}(1,idx) = 1-1/m(k)*(dot( D(:,k),X ) + ...
+                                dot( m(k)-j, p{k}(j+1,idx) ) );
+      endfor
+    
+      if ( n_bar(C) == N(C) )
+	break;
+      endif
+
+      ## Advance to next feasible configuration (Algorithm 3c, p. 10 Schwetman)
+      i = 1;
+      sw = true;
+      while sw
+	if ( ( mp==n || n_bar(i)==const(i)) && ( i<C ) )
+          mp -= n_bar(i);
+          n_bar(i) = 0;
+          i += 1;
+	else
+          n_bar(i)=n_bar(i)+1;
+          mp += 1;
+          sw = false;
+	endif
+      endwhile      
+    endwhile
+    Q = Q_next;
+  endfor
+  U = diag(X)*D; # U(c,k) = X(c)*D(c,k)
+  Q = diag(X)*(R.*V);
+  X = diag(X)*V;
+endfunction
+
+##############################################################################
+## Compute the linear index corresponding to vector i from a population
+## of N.
+function idx = __getidx( N, i )
+  if ( length(N) == 1 )
+    idx = 1;
+  else
+    i_cell = num2cell( (i+1)(1:end-1) );
+    idx = sub2ind( (N+1)(1:end-1), i_cell{:} );
+  endif
+endfunction
+
+%!test
+%! S = [1 1 2; 1 1 1];
+%! V = [1 1 1; 1 1 1];
+%! N = [1 1];
+%! m = [1 1 2];
+%! fail( "qnclosedmultimva(N)" );
+%! fail( "qnclosedmultimva(N,S,V,m)", "independent" );
+%! S = [0 0 0; 1 1 1];
+%! fail( "qnclosedmultimva(N,S,V,m)", "must contain at least" );
+%! S = [1 2 3; 1 2 3];
+%! N = [1 1];
+%! V = zeros(3,2,3);
+%! fail( "qnclosedmultimva(N,S,V)", "third parameter" );
+
+## Check degenerate case (population is zero); LI servers
+%!test
+%! S = [1 1 1; 1 1 1];
+%! N = [0 0];
+%! [U R Q X] = qnclosedmultimva(N, S);
+%! assert( U, 0*S );
+%! assert( R, 0*S );
+%! assert( Q, 0*S );
+%! assert( X, 0*S );
+
+## Check degenerate case (population is zero); LD servers
+%!test
+%! S = [1 1 1; 1 1 1];
+%! V = [1 1 1; 1 1 1];
+%! N = [0 0];
+%! m = [2 2 2];
+%! [U R Q X] = qnclosedmultimva(N, S, V, m);
+%! assert( U, 0*S );
+%! assert( R, 0*S );
+%! assert( Q, 0*S );
+%! assert( X, 0*S );
+
+## Example p. 142, Lazowska et al., "Quantitative System Performance:
+## Computer System Analysis Using Queueing Network Models"
+%!test
+%! S = [ 1/10 1/3; 2/5 1 ];
+%! V = [ 10 9; 5 4 ];
+%! N = [ 1 1 ];
+%! [U R Q X] = qnclosedmultimva(N,S,V);
+%! assert( Q, [ 4/19 15/19; 5/19 14/19 ], 1e-3 );
+%! assert( R .* V, [ 4/3 5; 5/2 7 ], 1e-3 );
+%! assert( diag( X ./ V )', [ 3/19 2/19 ], 1e-3 );
+%! assert( all(all(U<=1)) );
+
+## Example 8.3 p. 331, Bolch et al. Note that this is not a multiclass
+## network, because there is a single job class. Nevertheless, the
+## multiclass MVA algorithm must produce the same results as the single
+## class one.
+%!test
+%! S = [0.02 0.2 0.4 0.6];
+%! V = [1 0.4 0.2 0.1];
+%! N = [6];
+%! [U R Q X] = qnclosedmultimva( N, S, V );
+%! assert( Q, [0.244 2.261 2.261 1.234], 1e-3 );
+%! assert( R, [0.025 0.570 1.140 1.244], 1e-3 );
+%! assert( X, [9.920 3.968 1.984 0.992], 1e-3 );
+%! assert( U, [0.198 0.794 0.794 0.595], 1e-3 );
+
+## Example from table 3, p. 22, Herb Schwetman, "Implementing the Mean
+## Value Algorith for the Solution of Queueing Network Models",
+## Technical Report CSD-TR-355, Department of Computer Sciences, Purdue
+## University, feb 15, 1982.
+%!test
+%! S = [1 0 .025; 0 15 .5];
+%! V = [1 0 1; 0 1 1];
+%! N = [2 1];
+%! m = [-1 -1 1];
+%! [U R Q X] = qnclosedmultimva(N,S,V,m);
+%! assert( R(1,1), 1, 1e-3 );
+%! assert( R(2,2), 15, 1e-3 );
+%! assert( R(1,3), .027, 1e-3 );
+%! assert( R(2,3), .525, 1e-3  );
+%! assert( X(1,1)+X(1,2), 1.949, 1e-3 );
+%! assert( X(2,1)+X(2,2), 0.064, 1e-3 );
+%! assert( sum(Q,1), [1.949, .966, .085], 1e-3 );
+%! assert( all(U(:,3)<=1) );
+
+## Example from table 5, p. 23, Herb Schwetman, "Implementing the Mean
+## Value Algorith for the Solution of Queueing Network Models",
+## Technical Report CSD-TR-355, Department of Computer Sciences, Purdue
+## University, feb 15, 1982.  
+%!test
+%! S = [1 0 .025; 0 15 .5];
+%! V = [1 0 1; 0 1 1];
+%! N = [15 5];
+%! m = [-1 -1 1];
+%! [U R Q X] = qnclosedmultimva(N,S,V,m);
+%! # FIXME: I replaced 14.3->14.323
+%! assert( U, [14.323 0 .358; 0 4.707 .157], 1e-3 );
+%! # FIXME: I replaced 14.3->14.323
+%! assert( X, [14.323 0 14.323; 0 .314 .314 ], 1e-3 );
+%! # FIXME: I replaced 14.3->14.323
+%! assert( Q, [14.323 0 .677; 0 4.707 .293 ], 1e-3 );
+%! assert( R, [1 0 .047; 0 15 .934 ], 1e-3 );
+
+## Example 9.5 p. 337, Bolch et al.
+%!test
+%! S = [ 0.2 0.4 1; 0.2 0.6 2 ];
+%! V = [ 1 0.6 0.4; 1 0.3 0.7 ];
+%! N = [ 2 1 ];
+%! m = [ 2 1 -1 ];
+%! [U R Q X] = qnclosedmultimva(N,S,V,m);
+%! assert( Q, [ 0.428 0.726 0.845; 0.108 0.158 0.734 ], 1e-3 );
+%! assert( X(1,1), 2.113, 1e-3 ); # CHECK
+%! assert( X(2,1), 0.524, 1e-3 ); # CHECK
+%! assert( all( all(U<=1) ) );
+
+## Multiclass network with two classes; however, class 2 has 0 requests.
+## Therefore, we check that the results for class 1 are the same as those
+## computed by the single-class MVA
+%!test
+%! C = 2; # two classes
+%! K = 4; # four servers
+%! S = V = zeros(C,K);
+%! S(1,:) = linspace(1,2,K);
+%! S(2,:) = linspace(2,3,K);
+%! V(1,:) = linspace(4,1,K);
+%! V(2,:) = linspace(6,3,K);
+%! N = [10 0]; # class 2 has no customers
+%! [U1 R1 Q1 X1] = qnclosedmultimva(N,S,V);
+%! [U2 R2 Q2 X2] = qnclosedsinglemva(N(1),S(1,:),V(1,:));
+%! assert( U1(1,:), U2, 1e-5 );
+%! assert( R1(1,:), R2, 1e-5 );
+%! assert( Q1(1,:), Q2, 1e-5 );
+%! assert( X1(1,:), X2, 1e-5 );
+
+## This is example 5(b) page 7 of 
+## http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1258&context=cstech
+## "Testing network-of-queues software", technical report CSD-TR 330,
+%!test
+%! Z = [1 15];
+%! V = [1; 1];
+%! S = [.025; .5];
+%! N = [15; 5];
+%! [U R Q X] = qnclosedmultimva(N, S, V, 1, Z);
+%! assert( U, [.358; .157], 1e-3 );
+%! assert( Q, [.677; .293], 1e-3 );
+%! assert( X, [14.323; .314], 1e-3 ); ## NOTE: X(1,1) = 14.3 in Schwetman
+%! assert( R, [.047; .934], 1e-3 );
+
+## This is example of Figure 6, page 9 of
+## http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1258&context=cstech
+## "Testing network-of-queues software", technical report CSD-TR 330,
+%!test
+%! C = 2;
+%! K = 6;
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = P(2,1,2,2) = 1;
+%! P(1,2,1,3) = P(1,2,1,4) = P(1,2,1,5) = P(1,2,1,6) = .25;
+%! P(2,2,2,3) = P(2,2,2,4) = P(2,2,2,5) = P(2,2,2,6) = .25;
+%! P(1,3,1,1) = P(1,4,1,1) = P(1,5,1,1) = P(1,6,1,1) = .9;
+%! P(1,3,1,2) = P(1,4,1,2) = P(1,5,1,2) = P(1,6,1,2) = .1;
+%! P(2,3,2,1) = P(2,4,2,1) = P(2,5,2,1) = P(2,6,2,1) = .05;
+%! P(2,3,2,2) = P(2,4,2,2) = P(2,5,2,2) = P(2,6,2,2) = .95;
+%! N = [40 4];
+%! S = [ 5.0 .010 .035 .035 .035 .035; \
+%!      10.0 .100 .035 .035 .035 .035 ];
+%! V = qnvisits(P);
+%! [U R Q X] = qnclosedmultimva(N, S, V, [-1 1 1 1 1 1]);
+%! # FIXME: The results below were computed with JMVA; the numbers
+%! # in the paper are different (wrong?!?)!!
+%! assert( U, [39.457941 0.087684 0.076724 0.076724 0.076724 0.076724; \
+%!              2.772704 0.554541 0.048522 0.048522 0.048522 0.048522 ], 1e-5 );
+%! assert( R.*V, [5 0.024363 0.011081 0.011081 0.011081 0.011081; \
+%!                10 3.636155 0.197549 0.197549 0.197549 0.197549 ], 1e-5 );
+%! assert( Q(:,1), [39.457941 2.772704]', 1e-5 );
+%! assert( Q(:,2), [0.192262 1.008198]', 1e-5 );
+%! assert( Q(:,3), [0.087449 0.054775]', 1e-5 );
+%! assert( Q(:,4), Q(:,5), 1e-5 );
+%! assert( Q(:,5), Q(:,6), 1e-5 );
+%! assert( X(:,1), [7.891588 0.277270]', 1e-5 );
+%! assert( X(:,2), [8.768431 5.545407]', 1e-5 );
+%! assert( X(:,3), [2.192108 1.386352]', 1e-5 );
+%! assert( X(:,4), X(:,5), 1e-5 );
+%! assert( X(:,5), X(:,6), 1e-5 );
+
+
+## If there is no class switching, we must get the same results as
+## the plain application of multiclass MVA
+%!test
+%! C = 2; # two classes
+%! K = 4; # four servers
+%! C = 2; K = 4;
+%! P = zeros(C,K,C,K);
+%! S = zeros(C,K);
+%!
+%! # Routing
+%!
+%! # class 1 routing
+%! P(1,1,1,1) = .05;
+%! P(1,1,1,2) = .45;
+%! P(1,1,1,3) = .5;
+%! P(1,2,1,1) = 1;
+%! P(1,3,1,1) = 1;
+%! # class 2 routing
+%! P(2,1,2,1) = .01;
+%! P(2,1,2,3) = .5;
+%! P(2,1,2,4) = .49;
+%! P(2,3,2,1) = 1;
+%! P(2,4,2,1) = 1;
+%!
+%! # Compute visits
+%!
+%! V = qnvisits(P);
+%!
+%! # Define population and service times
+%! 
+%! N = [3 2];
+%! S = [0.01 0.09 0.10 0.08; \
+%!      0.05 0.09 0.10 0.08];
+%! [U1 R1 Q1 X1] = qnclosedmultimva(N,S,V); # this invokes __qnclosedmultimva_nocs
+%! [U2 R2 Q2 X2] = qnclosedmultimva(N,S,P); # this invokes __qnclosedmultimva_cs
+%! assert( U2, U1, 1e-5 );
+%! assert( R2, R1, 1e-5 );
+%! assert( Q2, Q1, 1e-5 );
+%! assert( X2, X1, 1e-5 );
+
+## Example from table 5, p. 23, Herb Schwetman, "Implementing the Mean
+## Value Algorith for the Solution of Queueing Network Models",
+## Technical Report CSD-TR-355, Department of Computer Sciences, Purdue
+## University, feb 15, 1982. Note: below, server 1 is the PS
+## node labeled "Sys 3" in the example; server 2 is the IS labeled
+## "APL1" and server e is the IS labeled "IMS2"
+%!test
+%! S = [.025 1 15; .5 1 15 ];
+%! P = zeros(2,3,2,3);
+%! P(1,1,1,2) = P(1,2,1,1) = 1;
+%! P(2,1,2,3) = P(2,3,2,1) = 1;
+%! ## V = qnvisits(P);
+%! N = [15 5];
+%! m = [1 -1 -1];
+%! [U R Q X] = qnclosedmultimva(N,S,P,m);
+%! # FIXME: I replaced 14.3->14.323
+%! assert( U, [0.358 14.323 0; 0.156 0 4.707], 1e-3 );
+%! # FIXME: I replaced 14.3->14.323
+%! assert( X, [14.323 14.3230 0; .314 0 .314 ], 1e-3 );
+%! # FIXME: I replaced 14.3->14.323
+%! assert( Q, [.677 14.323 0; .293 0 4.707], 1e-3 );
+%! assert( R, [.047 1 15.0; .934 1 15.0], 1e-3 );
+
+## Example figure 9 Schwetman
+%!test
+%! C = 2; K = 3;
+%! S = [.01 .07 .10; \
+%!      .05 .07 .10 ];
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = .7;
+%! P(1,1,1,3) = .2;
+%! P(1,1,2,1) = .1;
+%! P(2,1,2,2) = .3;
+%! P(2,1,2,3) = .5;
+%! P(2,1,1,1) = .2;
+%! P(1,2,1,1) = P(2,2,2,1) = 1;
+%! P(1,3,1,1) = P(2,3,2,1) = 1;
+%! N = [3 0];
+%! V = qnvisits(P);
+%! [U R Q X] = qnclosedmultimva(N, S, P);
+%! assert( R, [.015 .133 .163; .073 .133 .163], 1e-3 );
+%! assert( X, [12.609 8.826 2.522; 6.304 1.891 3.152], 1e-3 );
+%! assert( Q, [.185 1.175 .412; .462 .252 .515], 1e-3 );
+%! assert( U, [.126 .618 .252; .315 .132 .315], 1e-3 );
+
+## This example is from G. Casale and G. Serazzi. Quantitative system
+## evaluation with java modeling tools. In Proceedings of the second
+## joint WOSP/SIPEW international conference on Performance engineering,
+## ICPE '11, pages 449-454, New York, NY, USA, 2011. ACM
+%!demo
+%! Ntot = 100; # total population size
+%! b = linspace(0.1,0.9,10); # fractions of class-1 requests
+%! S = [20 80 31 14 23 12; \
+%!      90 30 33 20 14 7];
+%! V = ones(size(S));
+%! X1 = X1 = XX = zeros(size(b));
+%! R1 = R2 = RR = zeros(size(b));
+%! for i=1:length(b)
+%!   N = [fix(b(i)*Ntot) Ntot-fix(b(i)*Ntot)];
+%!   # printf("[%3d %3d]\n", N(1), N(2) );
+%!   [U R Q X] = qnclosedmultimva( N, S, V );
+%!   X1(i) = X(1,1) / V(1,1);
+%!   X2(i) = X(2,1) / V(2,1);
+%!   XX(i) = X1(i) + X2(i);
+%!   R1(i) = dot(R(1,:), V(1,:));
+%!   R2(i) = dot(R(2,:), V(2,:));
+%!   RR(i) = Ntot / XX(i);
+%! endfor
+%! subplot(2,1,1);
+%! plot(b, X1, "linewidth", 2, \
+%!      b, X2, "linewidth", 2, \
+%!      b, XX, "linewidth", 2 );
+%! legend("location","south");
+%! ylabel("Throughput");
+%! subplot(2,1,2);
+%! plot(b, R1, ";Class 1;", "linewidth", 2, \
+%!      b, R2, ";Class 2;", "linewidth", 2, \
+%!      b, RR, ";System;", "linewidth", 2 );
+%! legend("location","south");
+%! xlabel("Population mix \\beta for Class 1");
+%! ylabel("Resp. Time");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosedmultimvaapprox.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,260 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimvaapprox (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimvaapprox (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimvaapprox (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimvaapprox (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedmultimvaapprox (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol}, @var{iter_max})
+##
+## @cindex Mean Value Analysys (MVA), approximate
+## @cindex Approximate MVA
+## @cindex Closed network, multiple classes
+## @cindex Closed network, approximate analysis
+##
+## Analyze closed, multiclass queueing networks with @math{K} service
+## centers and @math{C} customer classes using the approximate Mean
+## Value Analysys (MVA) algorithm.
+##
+## This implementation uses Bard and Schweitzer approximation. It is based
+## on the assumption that
+## @iftex
+## @tex
+## $$Q_i({\bf N}-{\bf 1}_c) \approx {n-1 \over n} Q_i({\bf N})$$
+## @end tex
+## @end iftex
+## @ifnottex
+## the queue length at service center @math{k} with population
+## set @math{{\bf N}-{\bf 1}_c} is approximately equal to the queue length 
+## with population set @math{\bf N}, times @math{(n-1)/n}:
+##
+## @example
+## @group
+## Q_i(N-1c) ~ (n-1)/n Q_i(N)
+## @end group
+## @end example
+## @end ifnottex
+##
+## where @math{\bf N} is a valid population mix, @math{{\bf N}-{\bf 1}_c}
+## is the population mix @math{\bf N} with one class @math{c} customer
+## removed, and @math{n = \sum_c N_c} is the total number of requests.
+##
+## This implementation works for networks made of infinite server (IS)
+## nodes and single-server nodes only.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## @code{@var{N}(c)} is the number of
+## class @math{c} requests in the system (@code{@var{N}(c)>0}).
+##
+## @item S
+## @code{@var{S}(c,k)} is the mean service time for class @math{c}
+## customers at center @math{k} (@code{@var{S}(c,k) @geq{} 0}).
+##
+## @item V
+## @code{@var{V}(c,k)} is the average number of visits of class @math{c}
+## requests to center @math{k} (@code{@var{V}(c,k) @geq{} 0}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at service center
+## @math{k}. If @code{@var{m}(k) < 1}, then the service center @math{k}
+## is assumed to be a delay center (IS). If @code{@var{m}(k) == 1},
+## service center @math{k} is a regular queueing center (FCFS, LCFS-PR
+## or PS) with a single server node. If omitted, each service center has
+## a single server. Note that multiple server nodes are not supported.
+## 
+## @item Z
+## @code{@var{Z}(c)} is the class @math{c} external delay. Default
+## is 0.
+##
+## @item tol
+## Stopping tolerance (@code{@var{tol}>0}). The algorithm stops if
+## the queue length computed on two subsequent iterations are less than
+## @var{tol}. Default is @math{10^{-5}}.
+##
+## @item iter_max
+## Maximum number of iterations (@code{@var{iter_max}>0}.
+## The function aborts if convergenge is not reached within the maximum
+## number of iterations. Default is 100.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## If @math{k} is a FCFS, LCFS-PR or PS node, then @code{@var{U}(c,k)}
+## is the utilization of class @math{c} requests on service center
+## @math{k}. If @math{k} is an IS node, then @code{@var{U}(c,k)} is the
+## class @math{c} @emph{traffic intensity} at device @math{k},
+## defined as @code{@var{U}(c,k) = @var{X}(c)*@var{S}(c,k)}
+##
+## @item R
+## @code{@var{R}(c,k)} is the response
+## time of class @math{c} requests at service center @math{k}.
+##
+## @item Q
+## @code{@var{Q}(c,k)} is the average number of
+## class @math{c} requests at service center @math{k}.
+##
+## @item X
+## @code{@var{X}(c,k)} is the class @math{c}
+## throughput at service center @math{k}.
+##
+## @end table
+##
+## @seealso{qnclosed}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnclosedmultimvaapprox( N, S, V, m, Z, \
+                                             tol, \
+                                             iter_max )
+
+  if ( nargin < 3 || nargin > 7 )
+    print_usage();
+  endif
+
+  isvector(N) && all( N>=0 ) || \
+      usage( "N must be a vector of positive integers" );
+  N = N(:)'; # make N a row vector
+  C = length(N); ## Number of classes
+  K = columns(S); ## Number of service centers
+  size(S) == [C,K] || \
+      usage( "S size mismatch" );
+  size(V) == [C,K] || \
+      usage( "V size mismatch" );
+
+  if ( nargin < 4 )
+    m = ones(1,K);
+  else
+    isvector(m) || \
+	usage( "m must be a vector");
+    m = m(:)'; # make m a row vector
+    ( length(m) == K && all( m <= 1 ) ) || \
+        usage( "m must be <= 1 and have %d elements", K );
+  endif
+
+  if ( nargin < 5 )
+    Z = zeros(1,C);
+  else
+    isvector(Z) || \
+	usage( "Z must be a vector" );
+    Z = Z(:)'; # make Z a row vector
+    ( length(Z) == C && all(Z >= 0 ) ) || \
+	usage( "Z must be >= 0 and have %d elements", C );
+  endif
+
+  if ( nargin < 6 )
+    tol = 1e-5;
+  endif
+
+  if ( nargin < 7 )
+    iter_max = 100;
+  endif
+
+  ## Check consistency of parameters
+  all( all( S >= 0 ) ) || \
+      usage( "S contains negative values" );
+  all( all( V >= 0 ) ) || \
+      usage( "V contains negative values" );
+
+  ## Initialize results
+  R = zeros( C, K );
+  Xc = zeros( 1, C ); # Xc(c) is the class c throughput
+  Q = zeros( C, K );
+  D = V .* S;
+
+  ## Initialization of temporaries
+  iter = 0;
+  A = zeros( C, K );
+  Q = diag(N/K)*ones(C,K); # Q(c,k) = N(c) / K
+
+  i_single=find(m==1);
+  i_multi=find(m<1);
+  ## Main loop
+  N(N==0)=1;
+  do
+    iter++;
+    Qold = Q;
+  
+    ## A(c,k) = (N(c)-1)/N(c) * Q(c,k) + sum_{j=1, j|=c}^C Qold(j,k)
+    A = diag( (N-1) ./ N )*Q + ( (1 - eye(C)) * Qold ); 
+
+    ## R(c,k) = 
+    ##  S(c,k)                  is k is a delay center
+    ##  S(c,k) * (1+A(c,k))     if k is a queueing center; 
+    R(:,i_multi) = S(:,i_multi);
+    R(:,i_single) = S(:,i_single) .* ( 1 + A(:,i_single));
+
+    ## X(c) = N(c) / (sum_k R(c,k) * V(c,k))
+    Xc = N ./ (Z .+ sum(R.*V,2)'); 
+
+    ## Q(c,k) = X(c) * R(c,k) * V(c,k)
+    Q = (diag(Xc)*R).*V;
+
+    ## err = norm(Q-Qold);
+    err = norm((Q-Qold)./Qold, "inf");
+  until (err<tol || iter>iter_max);
+
+  if ( iter > iter_max ) 
+    warning( "qnclosedmultimvaapprox(): Convergence not reached after %d iterations", iter_max );
+  endif
+  X = diag(Xc)*V; # X(c,k) = X(c) * V(c,k)
+  U = diag(Xc)*D; # U(c,k) = X(c) * D(c,k)
+
+  # U(N==0,:) = R(N==0,:) = Q(N==0,:) = X(N==0,:) = 0;
+
+endfunction
+%!test
+%! S = [ 1 3 3; 2 4 3];
+%! V = [ 1 1 3; 1 1 3];
+%! N = [ 1 1 ];
+%! m = [1 ; 1 ];
+%! Z = [2 2 2];
+%! fail( "qnclosedmultimvaapprox(N,S,V,m,Z)", "m must be" );
+%! m = [1 ; 1 ; 1];
+%! fail( "qnclosedmultimvaapprox(N,S,V,m,Z)", "Z must be" );
+
+%!test
+%! S = [ 1 3; 2 4];
+%! V = [ 1 1; 1 1];
+%! N = [ 1 1 ];
+%! m = ones(1,2);
+%! [U R Q X] = qnclosedmultimvaapprox(N,S,V,m);
+%! assert( Q, [ .192 .808; .248 .752 ], 1e-3 );
+%! Xc = ( X(:,1)./V(:,1) )';
+%! assert( Xc, [ .154 .104 ], 1e-3 );
+%! # Compute the (overall) class-c system response time
+%! R_c = N ./ Xc;
+%! assert( R_c, [ 6.508 9.614 ], 5e-3 );
+
+%!demo
+%! S = [ 1, 1, 1, 1; 2, 1, 3, 1; 4, 2, 3, 3 ];
+%! V = ones(3,4);
+%! N = [10 5 1];
+%! m = [1 0 1 1];
+%! [U R Q X] = qnclosedmultimvaapprox(N,S,V,m);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosedpb.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,110 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{Xl}, @var{Xu}] =} qnclosedpb (@var{N}, @var{D} )
+##
+## Compute PB Bounds (C. H. Hsieh and S. Lam, 1987) 
+## for single-class, closed Queueing Networks
+## with @math{K} service centers.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## number of requests in the system (scalar). Must be @code{@var{N} > 0}.
+##
+## @item D
+## @code{@var{D}(k)} is the service demand of service center @math{k}. Must be
+## @code{@var{D}(k) @geq{} 0} for all @math{k}.
+##
+## @item Z
+## external delay (think time, scalar). If omitted, it is assumed to be zero.
+## Must be @code{@var{Z} @geq{} 0}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @itemx Xu
+## Lower and upper bounds on the system throughput.
+##
+## @end table
+##
+## @seealso{qnclosedab, qbclosedbsb, qnclosedgb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_lower X_upper] = qnclosedpb( N, D, Z )
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+  ( isscalar(N) && N > 0 ) || \
+      usage( "N must be a positive integer" );
+  ( isvector(D) && length(D)>0 && all( D >= 0 ) ) || \
+      usage( "D must be a vector of nonnegative floats" );
+  if ( nargin < 3 )
+    Z = 0;
+  else
+    ( isscalar(Z) && Z >= 0 ) || \
+        usage( "Z must be a nonnegative scalar" );
+  endif
+  D_tot = sum(D);
+  X_max = 1/max(D);
+  X_min = 0;
+  X_lower = N/( Z + D_tot + ...
+               ( sum( D .^ N * (N-1-Z*X_min) ) / sum( D .^ (N-1) ) ) );
+  X_upper = N/( Z + D_tot + ...
+               ( sum( D .^ 2 * (N-1-Z*X_max) ) / sum( D ) ) );
+  X_upper = min( X_upper, X_max ); # cap X upper bound to 1/max(D)
+endfunction
+
+%!test
+%! fail( "qnclosedpb( 1, [] )", "vector" );
+%! fail( "qnclosedpb( 1, [0 -1])", "vector" );
+%! fail( "qnclosedpb( 0, [1 2] )", "positive integer" );
+%! fail( "qnclosedpb( -1, [1 2])", "positive integer" );
+%! fail( "qnclosedpb( 1, [1 2], -1)", "nonnegative scalar" );
+
+%!# shared test function
+%!function test_pb( D, expected, Z=0 )
+%! for i=1:rows(expected)
+%!   N = expected(i,1);
+%!   [X_lower X_upper] = qnclosedpb(N,D,Z);
+%!   X_exp_lower = expected(i,2);
+%!   X_exp_upper = expected(i,3);
+%!   assert( [N X_lower X_upper], [N X_exp_lower X_exp_upper], 1e-4 )
+%! endfor
+
+%!test
+%! # table IV
+%! D = [ 0.1 0.1 0.09 0.08 ];
+%! #            N  X_lower  X_upper
+%! expected = [ 2  4.3174   4.3174; ... 
+%!              5  6.6600   6.7297; ...
+%!              10 8.0219   8.2700; ...
+%!              20 8.8672   9.3387; ...
+%!              80 9.6736   10.000 ];
+%! test_pb(D, expected);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosedsinglemva.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,364 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qnclosedsinglemva (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qnclosedsinglemva (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qnclosedsinglemva (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+##
+## @cindex Mean Value Analysys (MVA)
+## @cindex closed network, single class
+## @cindex normalization constant
+##
+## Analyze closed, single class queueing networks using the exact Mean
+## Value Analysis (MVA) algorithm. The following queueing disciplines
+## are supported: FCFS, LCFS-PR, PS and IS (Infinite Server). This
+## function supports fixed-rate service centers or multiple server
+## nodes. For general load-dependent service centers, use the function
+## @code{qnclosedsinglemvald} instead.
+##
+## Additionally, the normalization constant @math{G(n)}, @math{n=0,
+## @dots{}, N} is computed; @math{G(n)} can be used in conjunction with
+## the BCMP theorem to compute steady-state probabilities.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## Population size (number of requests in the system, @code{@var{N} @geq{} 0}).
+## If @code{@var{N} == 0}, this function returns
+## @code{@var{U} = @var{R} = @var{Q} = @var{X} = 0}
+##
+## @item S
+## @code{@var{S}(k)} is the mean service time on server @math{k}
+## (@code{@var{S}(k)>0}).
+##
+## @item V
+## @code{@var{V}(k)} is the average number of visits to service center
+## @math{k} (@code{@var{V}(k) @geq{} 0}).
+##
+## @item Z
+## External delay for customers (@code{@var{Z} @geq{} 0}). Default is 0.
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center @math{k}
+## (if @var{m} is a scalar, all centers have that number of servers). If
+## @code{@var{m}(k) < 1}, center @math{k} is a delay center (IS);
+## otherwise it is a regular queueing center (FCFS, LCFS-PR or PS) with
+## @code{@var{m}(k)} servers. Default is @code{@var{m}(k) = 1} for all
+## @math{k} (each service center has a single server).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## If @math{k} is a FCFS, LCFS-PR or PS node (@code{@var{m}(k) == 1}),
+## then @code{@var{U}(k)} is the utilization of center @math{k}. If
+## @math{k} is an IS node (@code{@var{m}(k) < 1}), then
+## @code{@var{U}(k)} is the @emph{traffic intensity} defined as
+## @code{@var{X}(k)*@var{S}(k)}.
+##
+## @item R
+## @code{@var{R}(k)} is the response time at center @math{k}.
+## The system response time @var{Rsys}
+## can be computed as @code{@var{Rsys} = @var{N}/@var{Xsys} - Z}
+##
+## @item Q
+## @code{@var{Q}(k)} is the average number of requests at center
+## @math{k}. The number of requests in the system can be computed
+## either as @code{sum(@var{Q})}, or using the formula
+## @code{@var{N}-@var{Xsys}*@var{Z}}.
+##
+## @item X
+## @code{@var{X}(k)} is the throughput of center @math{k}. The
+## system throughput @var{Xsys} can be computed as
+## @code{@var{Xsys} = @var{X}(1) / @var{V}(1)}
+##
+## @item G
+## Normalization constants. @code{@var{G}(n+1)} corresponds to the value
+## of the normalization constant @math{G(n)}, @math{n=0, @dots{}, N} as
+## array indexes in Octave start from 1. @math{G(n)} can be used in
+## conjunction with the BCMP theorem to compute steady-state
+## probabilities.
+##
+## @end table
+##
+## @seealso{qnclosedsinglemvald}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X G] = qnclosedsinglemva( N, S, V, m, Z )
+
+  if ( nargin < 3 || nargin > 5 ) 
+    print_usage();
+  endif
+
+  isscalar(N) && N >= 0 || \
+      usage( "N must be >= 0" );
+  isvector(S) || \
+      usage( "S must be a vector" );
+  S = S(:)'; # make S a row vector
+
+  isvector(V) || \
+      usage( "V must be a vector" );
+  V = V(:)'; # make V a row vector
+
+  K = length(S); # Number of servers
+
+  if ( nargin < 4 ) 
+    m = ones(1,K);
+  else
+    isvector(m) || \
+	usage( "m must be a vector" );
+    m = m(:)'; # make m a row vector
+  endif
+
+  [err S V m] = common_size(S, V, m);
+  (err == 0) || \
+      usage( "S, V and m are of incompatible size" );
+  all(S>=0) || \
+      usage( "S must be a vector >= 0" );
+  all(V>=0) || \
+      usage( "V must be a vector >= 0" );
+
+  if ( nargin < 5 )
+    Z = 0;
+  else
+    (isscalar(Z) && Z >= 0) || \
+        usage( "Z must be >= 0" );
+  endif
+
+  U = R = Q = X = zeros( 1, K );
+  G = zeros(1,N+1); G(1) = 1;
+  if ( N == 0 ) # Trivial case of empty population: just return all zeros
+    return;
+  endif
+
+  ## Initialize results
+  p = zeros( K, max(m)+1 ); # p(i,j+1) is the probability that there are j jobs at server i
+  p(:,1) = 1;
+  X_s = 0; # System throughput
+
+  i_single = find( m==1 );
+  i_multi = find( m>1 );
+  i_delay = find( m<1 );
+
+  ## Main MVA loop, iterates over the population size
+  for n=1:N 
+
+    R(i_single) = S(i_single) .* (1 + Q(i_single)); 
+    for i=i_multi # I cannot easily vectorize this
+      j=0:m(i)-2;
+      R(i) = S(i) / m(i) * (1+Q(i)+dot( m(i)-j-1, p( i, 1+j ) ) );
+    endfor
+    R(i_delay) = S(i_delay);
+
+    R_s = dot( V, R ); # System response time
+    X_s = n / ( Z + R_s ); # System Throughput
+    Q = X_s * ( V .* R );
+    G(1+n) = G(n) / X_s;
+    
+    ## prepare for next iteration
+    lambda_i = V * X_s; # lambda_i(i) is the node i throughput
+    for i=i_multi
+      j=1:m(i)-1; # range
+      p(i, j+1) = lambda_i(i) .* S(i) ./ min( j,m(i) ) .* p(i,j);
+      p(i,1) = 1 - 1/m(i) * ...
+          (V(i)*S(i)*X_s + dot( m(i)-j, p(i,j+1)) );
+    endfor
+
+  endfor
+  X = X_s * V; # Service centers throughput
+  U(i_single) = X(i_single) .* S(i_single);
+  U(i_delay) = X(i_delay) .* S(i_delay);
+  U(i_multi) = X(i_multi) .* S(i_multi) ./ m(i_multi);
+endfunction
+
+#{
+
+## This function is slightly faster (and more compact) than the above
+## when all servers are single-server or delay centers. Improvements are
+## quite small (10%-15% faster, depends on the network size), so at the
+## moment it is commented out.
+function [U R Q X G] = __qnclosedsinglemva_fast( N, S, V, m, Z )
+  U = R = Q = X = zeros( 1, length(S) );
+  X_s = 0; # System throughput
+  G = zeros(1,N+1); G(1) = 1;
+
+  ## Main MVA loop
+  for n=1:N 
+    R = S .* (1+Q.*(m==1));
+    R_s = dot( V, R ); # System response time
+    X_s = n / ( Z + R_s ); # System Throughput
+    Q = X_s * ( V .* R );
+    G(1+n) = G(n) / X_s;    
+  endfor
+  X = X_s * V; # Service centers throughput
+  U = X .* S;
+endfunction
+
+#}
+
+%!test
+%! fail( "qnclosedsinglemva()", "Invalid" );
+%! fail( "qnclosedsinglemva( 10, [1 2], [1 2 3] )", "S, V and m" );
+%! fail( "qnclosedsinglemva( 10, [-1 1], [1 1] )", ">= 0" );
+
+## Check degenerate case of N==0 (LI case)
+%!test
+%! N = 0;
+%! S = [1 2 3 4];
+%! V = [1 1 1 4];
+%! [U R Q X] = qnclosedsinglemva(N, S, V);
+%! assert( U, 0*S );
+%! assert( R, 0*S );
+%! assert( Q, 0*S );
+%! assert( X, 0*S );
+
+## Check degenerate case of N==0 (LD case)
+%!test
+%! N = 0;
+%! S = [1 2 3 4];
+%! V = [1 1 1 4];
+%! m = [2 3 4 5];
+%! [U R Q X] = qnclosedsinglemva(N, S, V, m);
+%! assert( U, 0*S );
+%! assert( R, 0*S );
+%! assert( Q, 0*S );
+%! assert( X, 0*S );
+
+%!test
+%! # Exsample 3.42 p. 577 Jain
+%! S = [ 0.125 0.3 0.2 ]';
+%! V = [ 16 10 5 ];
+%! N = 20;
+%! m = ones(1,3)';
+%! Z = 4;
+%! [U R Q X] = qnclosedsinglemva(N,S,V,m,Z);
+%! assert( R, [ .373 4.854 .300 ], 1e-3 );
+%! assert( Q, [ 1.991 16.177 0.500 ], 1e-3 );
+%! assert( all( U>=0 ) );
+%! assert( all( U<=1 ) );
+
+%!test
+%! # Exsample 3.42 p. 577 Jain
+%! S = [ 0.125 0.3 0.2 ];
+%! V = [ 16 10 5 ];
+%! N = 20;
+%! m = ones(1,3);
+%! Z = 4;
+%! [U R Q X] = qnclosedsinglemva(N,S,V,m,Z);
+%! assert( R, [ .373 4.854 .300 ], 1e-3 );
+%! assert( Q, [ 1.991 16.177 0.500 ], 1e-3 );
+%! assert( all( U>=0 ) );
+%! assert( all( U<=1 ) );
+
+%!test
+%! # Example 8.4 p. 333 Bolch et al.
+%! S = [ .5 .6 .8 1 ];
+%! N = 3;
+%! m = [2 1 1 -1];
+%! V = [ 1 .5 .5 1 ];
+%! [U R Q X] = qnclosedsinglemva(N,S,V,m);
+%! assert( Q, [ 0.624 0.473 0.686 1.217 ], 1e-3 );
+%! assert( X, [ 1.218 0.609 0.609 1.218 ], 1e-3 );
+%! assert( all(U >= 0 ) );
+%! assert( all(U( m>0 ) <= 1 ) );
+
+%!test
+%! # Example 8.3 p. 331 Bolch et al.
+%! # This is a single-class network, which however nothing else than
+%! # a special case of multiclass network
+%! S = [ 0.02 0.2 0.4 0.6 ];
+%! K = 6;
+%! V = [ 1 0.4 0.2 0.1 ];
+%! [U R Q X] = qnclosedsinglemva(K, S, V);
+%! assert( U, [ 0.198 0.794 0.794 0.595 ], 1e-3 );
+%! assert( R, [ 0.025 0.570 1.140 1.244 ], 1e-3 );
+%! assert( Q, [ 0.244 2.261 2.261 1.234 ], 1e-3 );
+%! assert( X, [ 9.920 3.968 1.984 0.992 ], 1e-3 );
+
+%!test
+%! # Check bound analysis
+%! N = 10; # max population
+%! for n=1:N
+%!   S = [1 0.8 1.2 0.5];
+%!   V = [1 2 2 1];
+%!   [U R Q X] = qnclosedsinglemva(n, S, V);
+%!   Xs = X(1)/V(1);
+%!   Rs = dot(R,V);
+%!   # Compare with balanced system bounds
+%!   [Xlbsb Xubsb Rlbsb Rubsb] = qnclosedbsb( n, S .* V );
+%!   assert( Xlbsb<=Xs );
+%!   assert( Xubsb>=Xs );
+%!   assert( Rlbsb<=Rs );
+%!   assert( Rubsb>=Rs );
+%!   # Compare with asymptotic bounds
+%!   [Xlab Xuab Rlab Ruab] = qnclosedab( n, S .* V );
+%!   assert( Xlab<=Xs );
+%!   assert( Xuab>=Xs );
+%!   assert( Rlab<=Rs );
+%!   assert( Ruab>=Rs );
+%! endfor
+
+## Example from Schwetman (figure 7, page 9 of
+## http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1258&context=cstech
+## "Testing network-of-queues software, technical report CSD-TR 330,
+## Purdue University). Note that the results for that network (table 9
+## of the reference above) seems to be wrong. The "correct" results
+## below have been computed using the multiclass MVA implementation of
+## JMT (http://jmt.sourceforge.net/)
+%!test
+%! V = [ 1.00 0.45 0.50 0.00; \
+%!       1.00 0.00 0.50 0.49 ];
+%! N = [3 2];
+%! S = [0.01 0.09 0.10 0.08; \
+%!      0.05 0.09 0.10 0.08];
+%! [U R Q X] = qnclosedmultimva(N, S, V);
+%! assert( U, [ 0.1215 0.4921 0.6075 0.0000; \
+%!              0.3433 0.0000 0.3433 0.2691 ], 1e-4 );
+%! assert( Q, [ 0.2131 0.7539 2.0328 0.0000; \
+%!              0.5011 0.0000 1.1839 0.3149 ], 1e-4 );
+%! assert( R.*V, [0.0175 0.0620 0.1672 0.0000; \
+%!                0.0729 0.0000 0.1724 0.0458 ], 1e-4 );
+%! assert( X, [12.1517 5.4682 6.0758 0.0000; \
+%!              6.8669 0.0000 3.4334 3.3648 ], 1e-4 );
+
+%!demo
+%! S = [ 0.125 0.3 0.2 ];
+%! V = [ 16 10 5 ];
+%! N = 20;
+%! m = ones(1,3);
+%! Z = 4;
+%! [U R Q X] = qnclosedsinglemva(N,S,V,m,Z);
+%! X_s = X(1)/V(1); # System throughput
+%! R_s = dot(R,V); # System response time
+%! printf("\t    Util      Qlen     RespT      Tput\n");
+%! printf("\t--------  --------  --------  --------\n");
+%! for k=1:length(S)
+%!   printf("Dev%d\t%8.4f  %8.4f  %8.4f  %8.4f\n", k, U(k), Q(k), R(k), X(k) );
+%! endfor
+%! printf("\nSystem\t          %8.4f  %8.4f  %8.4f\n\n", N-X_s*Z, R_s, X_s );
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosedsinglemvaapprox.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,237 @@
+## Copyright (C) 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedsinglemvaapprox (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedsinglemvaapprox (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedsinglemvaapprox (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedsinglemvaapprox (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedsinglemvaapprox (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol}, @var{iter_max})
+##
+## @cindex Mean Value Analysys (MVA), approximate
+## @cindex Approximate MVA
+## @cindex Closed network, single class
+## @cindex Closed network, approximate analysis
+##
+## Analyze closed, single class queueing networks using the Approximate
+## Mean Value Analysis (MVA) algorithm. This function is based on
+## approximating the number of customers seen at center @math{k} when a
+## new request arrives as @math{Q_k(N) \times (N-1)/N}. This function
+## only handles single-server and delay centers; if your network
+## contains general load-dependent service centers, use the function
+## @code{qnclosedsinglemvald} instead.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## Population size (number of requests in the system, @code{@var{N} > 0}).
+##
+## @item S
+## @code{@var{S}(k)} is the mean service time on server @math{k}
+## (@code{@var{S}(k)>0}).
+##
+## @item V
+## @code{@var{V}(k)} is the average number of visits to service center
+## @math{k} (@code{@var{V}(k) @geq{} 0}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center @math{k}
+## (if @var{m} is a scalar, all centers have that number of servers). If
+## @code{@var{m}(k) < 1}, center @math{k} is a delay center (IS); if
+## @code{@var{m}(k) == 1}, center @math{k} is a regular queueing
+## center (FCFS, LCFS-PR or PS) with one server (default). This function
+## does not support multiple server nodes (@code{@var{m}(k) > 1}).
+##
+## @item Z
+## External delay for customers (@code{@var{Z} @geq{} 0}). Default is 0.
+##
+## @item tol
+## Stopping tolerance. The algorithm stops when the maximum relative difference
+## between the new and old value of the queue lengths @var{Q} becomes
+## less than the tolerance. Default is @math{10^{-5}}.
+##
+## @item iter_max
+## Maximum number of iterations (@code{@var{iter_max}>0}.
+## The function aborts if convergenge is not reached within the maximum
+## number of iterations. Default is 100.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## If @math{k} is a FCFS, LCFS-PR or PS node (@code{@var{m}(k) == 1}),
+## then @code{@var{U}(k)} is the utilization of center @math{k}. If
+## @math{k} is an IS node (@code{@var{m}(k) < 1}), then
+## @code{@var{U}(k)} is the @emph{traffic intensity} defined as
+## @code{@var{X}(k)*@var{S}(k)}.
+##
+## @item R
+## @code{@var{R}(k)} is the response time at center @math{k}.
+## The system response time @var{Rsys}
+## can be computed as @code{@var{Rsys} = @var{N}/@var{Xsys} - Z}
+##
+## @item Q
+## @code{@var{Q}(k)} is the average number of requests at center
+## @math{k}. The number of requests in the system can be computed
+## either as @code{sum(@var{Q})}, or using the formula
+## @code{@var{N}-@var{Xsys}*@var{Z}}.
+##
+## @item X
+## @code{@var{X}(k)} is the throughput of center @math{k}. The
+## system throughput @var{Xsys} can be computed as
+## @code{@var{Xsys} = @var{X}(1) / @var{V}(1)}
+##
+## @end table
+##
+## @seealso{qnclosedsinglemva,qnclosedsinglemvald}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnclosedsinglemvaapprox( N, S, V, m, Z, tol, iter_max )
+
+  if ( nargin < 3 || nargin > 7 ) 
+    print_usage();
+  endif
+
+  isscalar(N) && N >= 0 || \
+      usage( "N must be >= 0" );
+  isvector(S) || \
+      usage( "S must be a vector" );
+  isvector(V) || \
+      usage( "V must be a vector" );
+  S = S(:)'; # make S a row vector
+  V = V(:)'; # make V a row vector
+
+  K = length(S); # Number of servers
+
+  if ( nargin < 4 ) 
+    m = ones(1,K);
+  else
+    isvector(m) || \
+	usage( "m must be a vector" );
+    m = m(:)'; # make m a row vector
+  endif
+
+  [err S V m] = common_size(S, V, m);
+  (err == 0) || \
+      usage( "S, V and m are of incompatible size" );
+  all(S>=0) || \
+      usage( "S must be a vector >= 0" );
+  all(V>=0) || \
+      usage( "V must be a vector >= 0" );
+  all(m<=1) || \
+      usage( "Vector m must be <= 1 (this function supports IS and single-server nodes only)" );
+
+  if ( nargin < 5 )
+    Z = 0;
+  else
+    (isscalar(Z) && Z >= 0) || \
+        usage( "Z must be >= 0" );
+  endif
+
+  if ( nargin < 6 )
+    tol = 1e-5;
+  else
+    ( isscalar(tol) && tol>0 ) || \
+	usage("tol must be a positive scalar");
+  endif
+
+  if ( nargin < 7 )
+    iter_max = 100;
+  else
+    ( isscalar(iter_max) && iter_max > 0 ) || \
+	usage("iter_max must be a positive integer");
+  endif
+
+  U = R = Q = X = zeros( 1, K );
+  ## Trivial case of empty population: just return all zeros
+  if ( N == 0 )
+    return;
+  endif
+
+  Q = N/K * ones(1,K); # initialize queue lengths
+  iter = 0;
+  do
+    iter++;
+    Qold = Q;
+    A = (N-1)/N * Q;
+    R = S.*(1+A.*(m==1));
+    Rs = dot(V,R);
+    Xs = N/(Z+Rs);
+    Q = Xs*(V.*R);
+    err = norm((Q-Qold)./Qold, "inf");
+  until (err < tol || iter>iter_max);
+  if ( iter > iter_max ) 
+    warning( "qnclosedsinglemvaapprox(): Convergence not reached after %d iterations", iter_max );
+  endif
+  X = Xs * V;
+  U = X .* S;  
+endfunction
+%!test
+%! fail( "qnclosedsinglemvaapprox()", "Invalid" );
+%! fail( "qnclosedsinglemvaapprox( 10, [1 2], [1 2 3] )", "S, V and m" );
+%! fail( "qnclosedsinglemvaapprox( 10, [-1 1], [1 1] )", ">= 0" );
+%! fail( "qnclosedsinglemvaapprox( 10, [1 2], [1 2], [1 2] )", "supports");
+%! fail( "qnclosedsinglemvaapprox( 10, [1 2], [1 2], [1 1], 0, -1)", "tol");
+
+%!test
+%! # Example p. 117 Lazowska et al.
+%! S = [0.605 2.1 1.35];
+%! V = [1 1 1];
+%! N = 3;
+%! Z = 15;
+%! m = 1;
+%! [U R Q X] = qnclosedsinglemvaapprox(N, S, V, m, Z);
+%! Rs = dot(V,R);
+%! Xs = N/(Z+Rs);
+%! assert( Q, [0.0973 0.4021 0.2359], 1e-3 );
+%! assert( Xs, 0.1510, 1e-3 );
+%! assert( Rs, 4.87, 1e-3 );
+
+%!demo
+%! S = [ 0.125 0.3 0.2 ];
+%! V = [ 16 10 5 ];
+%! N = 30;
+%! m = ones(1,3);
+%! Z = 4;
+%! Xmva = Xapp = Rmva = Rapp = zeros(1,N);
+%! for n=1:N
+%!   [U R Q X] = qnclosedsinglemva(n,S,V,m,Z);
+%!   Xmva(n) = X(1)/V(1);
+%!   Rmva(n) = dot(R,V);
+%!   [U R Q X] = qnclosedsinglemvaapprox(n,S,V,m,Z);
+%!   Xapp(n) = X(1)/V(1);
+%!   Rapp(n) = dot(R,V);
+%! endfor
+%! subplot(2,1,1);
+%! plot(1:N, Xmva, ";Exact;", "linewidth", 2, 1:N, Xapp, "x;Approximate;", "markersize", 7);
+%! legend("location","southeast");
+%! ylabel("Throughput X(n)");
+%! subplot(2,1,2);
+%! plot(1:N, Rmva, ";Exact;", "linewidth", 2, 1:N, Rapp, "x;Approximate;", "markersize", 7);
+%! legend("location","southeast");
+%! ylabel("Response Time R(n)");
+%! xlabel("Number of Requests n");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnclosedsinglemvald.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,198 @@
+## Copyright (C) 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedsinglemvald (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosedsinglemvald (@var{N}, @var{S}, @var{V}, @var{Z})
+##
+## @cindex Mean Value Analysys (MVA)
+## @cindex closed network, single class
+## @cindex load-dependent service center
+##
+## Exact MVA algorithm for closed, single class queueing networks
+## with load-dependent service centers. This function supports
+## FCFS, LCFS-PR, PS and IS nodes. For networks with only fixed-rate
+## service centers and multiple-server nodes, the function
+## @code{qnclosedsinglemva} is more efficient.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## Population size (number of requests in the system, @code{@var{N} @geq{} 0}).
+## If @code{@var{N} == 0}, this function returns @code{@var{U} = @var{R} = @var{Q} = @var{X} = 0}
+##
+## @item S
+## @code{@var{S}(k,n)} is the mean service time at center @math{k}
+## where there are @math{n} requests, @math{1 @leq{} n
+## @leq{} N}. @code{@var{S}(k,n)} @math{= 1 / \mu_{k,n}},
+## where @math{\mu_{k,n}} is the service rate of center @math{k}
+## when there are @math{n} requests.
+##
+## @item V
+## @code{@var{V}(k)} is the average number
+## of visits to service center @math{k} (@code{@var{V}(k) @geq{} 0}).
+##
+## @item Z
+## external delay ("think time", @code{@var{Z} @geq{} 0}); default 0.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## @code{@var{U}(k)} is the utilization of service center @math{k}. The
+## utilization is defined as the probability that service center
+## @math{k} is not empty, that is, @math{U_k = 1-\pi_k(0)} where
+## @math{\pi_k(0)} is the steady-state probability that there are 0
+## jobs at service center @math{k}.
+##
+## @item R
+## @code{@var{R}(k)} is the response time on service center @math{k}.
+##
+## @item Q
+## @code{@var{Q}(k)} is the average number of requests in service center
+## @math{k}.
+##
+## @item X
+## @code{@var{X}(k)} is the throughput of service center @math{k}.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnclosedsinglemvald( N, S, V, Z )
+
+  if ( nargin < 3 || nargin > 4 ) 
+    print_usage();
+  endif
+
+  isvector(V) && all(V>=0) || \
+      usage( "V must be a vector >= 0" );
+  V = V(:)'; # make V a row vector
+  K = length(V); # Number of servers
+  isscalar(N) && N >= 0 || \
+      usage( "N must be >= 0" );
+  ( ismatrix(S) && rows(S) == K && columns(S) >= N ) || \
+      usage( "S size mismatch: is %dx%d, should be %dx%d", rows(S), columns(S), K, N );
+  all(all(S>=0)) || \
+      usage( "S must be >= 0" );
+
+  if ( nargin < 4 ) 
+    Z = 0;
+  else
+    isscalar(Z) && Z>=0 || \
+        usage( "Z must be >= 0" );
+  endif
+
+  ## Initialize results
+  p = zeros( K, N+1 ); # p(k,i+1) is the probability that there are i jobs at server k, given that the network population is j
+  p(:,1) = 1;
+  U = R = Q = X = zeros( 1, K );
+  X_s = 0;              # System throughput
+
+  ## Main MVA loop, iterates over the population size
+  for n=1:N # over population size
+
+    j=1:n;
+    ## for i=1:K
+    ##   R(i) = sum( j.*S(i,j).*p(i,j) );
+    ## endfor
+    R = sum( repmat(j,K,1).*S(:,1:n).*p(:,1:n), 2)';
+
+    R_s = dot( V, R ); # System response time
+    X_s = n / (Z+R_s); # System Throughput
+    ## G_N = G_Nm1 / X_s; G_Nm1 = G_N;
+
+    ## prepare for next iteration
+    for i=1:K
+      p(i, 1+j) = X_s * S(i,j) .* p(i,j) * V(i);      
+      p(i, 1) = 1-sum(p(i,1+j));
+    endfor    
+  endfor
+  Q = X_s * ( V .* R );
+  U = 1-p(:,1)'; # Service centers utilization
+  X = X_s * V; # Service centers throughput
+endfunction
+
+## Check degenerate case of N==0 (general LD case)
+%!test
+%! N = 0;
+%! S = [1 2; 3 4; 5 6; 7 8];
+%! V = [1 1 1 4];
+%! [U R Q X] = qnclosedsinglemvald(N, S, V);
+%! assert( U, 0*V );
+%! assert( R, 0*V );
+%! assert( Q, 0*V );
+%! assert( X, 0*V );
+
+%!test
+%! # Exsample 3.42 p. 577 Jain
+%! V = [ 16 10 5 ];
+%! N = 20;
+%! S = [ 0.125 0.3 0.2 ];
+%! Sld = repmat( S', 1, N );
+%! Z = 4;
+%! [U1 R1 Q1 X1] = qnclosedsinglemvald(N,Sld,V,Z);
+%! [U2 R2 Q2 X2] = qnclosedsinglemva(N,S,V,ones(1,3),Z);
+%! assert( U1, U2, 1e-3 );
+%! assert( R1, R2, 1e-3 );
+%! assert( Q1, Q2, 1e-3 );
+%! assert( X1, X2, 1e-3 );
+
+%!test
+%! # Example 8.7 p. 349 Bolch et al.
+%! N = 3;
+%! Sld = 1 ./ [ 2 4 4; \
+%!              1.667 1.667 1.667; \
+%!              1.25 1.25 1.25; \
+%!              1 2 3 ];
+%! V = [ 1 .5 .5 1 ];
+%! [U R Q X] = qnclosedsinglemvald(N,Sld,V);
+%! assert( Q, [0.624 0.473 0.686 1.217], 1e-3 );
+%! assert( R, [0.512 0.776 1.127 1], 1e-3 );
+%! assert( all( U<=1) );
+
+%!test
+%! # Example 8.4 p. 333 Bolch et al.
+%! N = 3;
+%! S = [ .5 .6 .8 1 ];
+%! m = [2 1 1 N];
+%! Sld = zeros(3,N);
+%! Sld(1,:) = .5 ./ [1 2 2];
+%! Sld(2,:) = [.6 .6 .6];
+%! Sld(3,:) = [.8 .8 .8];
+%! Sld(4,:) = 1 ./ [1 2 3];
+%! V = [ 1 .5 .5 1 ];
+%! [U1 R1 Q1 X1] = qnclosedsinglemvald(N,Sld,V);
+%! [U2 R2 Q2 X2] = qnclosedsinglemva(N,S,V,m);
+%! ## Note that qnclosedsinglemvald computes the utilization in a different
+%! ## way as qnclosedsinglemva; in fact, qnclosedsinglemva knows that service
+%! ## center i has m(i)>1 servers, but qnclosedsinglemvald does not. Thus,
+%! ## utilizations for multiple-server nodes cannot be compared
+%! assert( U1([2,3]), U2([2,3]), 1e-3 );
+%! assert( R1, R2, 1e-3 );
+%! assert( Q1, Q2, 1e-3 );
+%! assert( X1, X2, 1e-3 );
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qncmva.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,170 @@
+## Copyright (C) 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmva (@var{N}, @var{S}, @var{Sld}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmva (@var{N}, @var{S}, @var{Sld}, @var{V}, @var{Z})
+##
+## @cindex Mean Value Analysys (MVA)
+## @cindex CMVA
+##
+## Implementation of the Conditional MVA (CMVA) algorithm, a numerically
+## stable variant of MVA for load-dependent servers. CMVA is described
+## in G. Casale, @cite{A Note on Stable Flow-Equivalent Aggregation in
+## Closed Networks}. The network is made of @math{M} service centers and
+## a delay center. Servers @math{1, \ldots, M-1} are load-independent;
+## server @math{M} is load-dependent.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## Population size (number of requests in the system, @code{@var{N} @geq{} 0}).
+## If @code{@var{N} == 0}, this function returns
+## @code{@var{U} = @var{R} = @var{Q} = @var{X} = 0}
+##
+## @item S
+## @code{@var{S}(k)} is the mean service time on server @math{k = 1, @dots{}, M-1}
+## (@code{@var{S}(k) > 0}).
+##
+## @item Sld
+## @code{@var{Sld}(n)} is the mean service time on server @math{M}
+## when there are @math{n} requests, @math{n=1, @dots{}, N}.
+## @code{@var{Sld}(n) = } @math{1 / \mu(n)}, where @math{\mu(n)} is the
+## service rate at center @math{N} when there are @math{n} requests.
+##
+## @item V
+## @code{@var{V}(k)} is the average number of visits to service center
+## @math{k= 1, @dots{}, M} (@code{@var{V}(k) @geq{} 0}).
+##
+## @item Z
+## External delay for customers (@code{@var{Z} @geq{} 0}). Default is 0.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## @code{@var{U}(k)} is the utilization of center @math{k=1, @dots{}, M}
+##
+## @item R
+## @code{@var{R}(k)} is the response time at center @math{k=1, @dots{}, M}.
+## The system response time @var{Rsys}
+## can be computed as @code{@var{Rsys} = @var{N}/@var{Xsys} - Z}
+##
+## @item Q
+## @code{@var{Q}(k)} is the average number of requests at center
+## @math{k=1, @dots{}, M}.
+##
+## @item X
+## @code{@var{X}(k)} is the throughput of center @math{k=1, @dots{}, M}.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qncmva( N, S, Sld, V, Z )
+
+  if ( nargin < 4 || nargin > 5 )
+    print_usage();
+  endif
+
+  isscalar(N) && N >= 0 || \
+      usage("N must be a positive scalar");
+
+  isvector(S) || \
+      usage("S must be a vector");
+  S = S(:)'; # make S a row vector
+  M = length(S)+1; # number of service centers (escluding the delay center)
+  
+  isvector(Sld) && length(Sld) == N && all(Sld>=0) || \
+      usage("Sld must be a vector with %d elements >= 0", N);
+  Sld = Sld(:)'; # Make Sld a row vector
+
+  isvector(V) && length(V) == M && all(V>=0) || \
+      usage("V must be a vector with %d elements", M);
+  V = V(:)'; # Make V a row vector
+
+  if ( nargin == 5 )
+    isscalar(Z) && Z>=0 || \
+	usage("Z must be nonnegative");
+  else
+    Z = 0;
+  endif
+
+  if ( N == 0 )
+    U = R = Q = X = zeros(1,M);
+  endif
+
+  D = S .* V(1:M-1); # service demands
+  Ri = Qi = zeros(M,N+1,N+1);
+  DM = Xs = zeros(N+1,N+1); # system throughput
+
+  ## Main MVA loop
+  for n=1:N 
+    for t=1:(N-n+1)
+      if ( n==1 )
+	DM(1+n,t) = Sld(t)*V(M);
+      else
+	DM(1+n,t) = Xs(n,t)/Xs(n,t+1)*DM(n,t);
+      endif
+      i=1:(M-1);
+      Ri(i,1+n,t) = D(i).*(1+Qi(i,n,t))';
+      Ri(M,1+n,t) = DM(1+n,t).*(1+Qi(M,n,t+1));
+      Xs(1+n,t) = n/(Z+sum(Ri(:,1+n,t)));
+      i=1:(M-1);
+      Qi(i,1+n,t) = D(i) .* Xs(1+n,t) .* (1+Qi(i,n,t))';
+      Qi(M,1+n,t) = DM(1+n,t).*Xs(1+n,t).*(1+Qi(M,n,t+1));
+    endfor
+  endfor
+  X = Xs(1+N,1)*V;
+  Q = Qi(:,1+N,1)';
+  R = Ri(:,1+N,1)';
+  U = [D DM(1+N,1)] .* X;
+endfunction
+%!test
+%! N=5;
+%! S = [1 0.3 0.8 0.9];
+%! V = [1 1 1 1];
+%! [U1 R1 Q1 X1] = qncmva( N, S(1:3), repmat(S(4),1,N), V );
+%! [U2 R2 Q2 X2] = qnclosedsinglemva(N, S, V);
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 1e-5 );
+%! assert( X1, X2, 1e-5 );
+
+%!test
+%! N=5;
+%! S = [1 1 1 1 1; \
+%!      1 1 1 1 1; \
+%!      1 1 1 1 1; \
+%!      1 1/2 1/3 1/4 1/5];
+%! V = [1 1 1 1];
+%! [U1 R1 Q1 X1] = qncmva( N, S(1:3,1), S(4,:), V );
+%! [U2 R2 Q2 X2] = qnclosedsinglemvald(N, S, V);
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 1e-5 );
+%! assert( X1, X2, 1e-5 );
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnconvolution.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,236 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qnconvolution (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qnconvolution (@var{N}, @var{S}, @var{V}, @var{m})
+##
+## @cindex closed network
+## @cindex normalization constant
+## @cindex convolution algorithm
+##
+## This function implements the @emph{convolution algorithm} for
+## computing steady-state performance measures of product-form,
+## single-class closed queueing networks. Load-independent service
+## centers, multiple servers (@math{M/M/m} queues) and IS nodes are
+## supported. For general load-dependent service centers, use the
+## @code{qnconvolutionld} function instead.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## Number of requests in the system (@code{@var{N}>0}).
+##
+## @item S
+## @code{@var{S}(k)} is the average service time on center @math{k}
+## (@code{@var{S}(k) @geq{} 0}).
+##
+## @item V
+## @code{@var{V}(k)} is the visit count of service center @math{k}
+## (@code{@var{V}(k) @geq{} 0}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center
+## @math{k}. If @code{@var{m}(k) < 1}, center @math{k} is a delay center (IS);
+## if @code{@var{m}(k) @geq{} 1}, center @math{k}
+## it is a regular @math{M/M/m} queueing center with @code{@var{m}(k)}
+## identical servers. Default is @code{@var{m}(k) = 1} for all @math{k}.
+##
+## @end table
+##
+## @strong{OUTPUT}
+##
+## @table @var
+##
+## @item U
+## @code{@var{U}(k)} is the utilization of center @math{k}. 
+## For IS nodes, @code{@var{U}(k)} is the @emph{traffic intensity}.
+##
+## @item R
+## @code{@var{R}(k)} is the average response time of center @math{k}.
+##
+## @item Q
+## @code{@var{Q}(k)} is the average number of customers at center
+## @math{k}.
+##
+## @item X
+## @code{@var{X}(k)} is the throughput of center @math{k}.
+##
+## @item G
+## Vector of normalization constants. @code{@var{G}(n+1)} contains the value of
+## the normalization constant with @math{n} requests
+## @math{G(n)}, @math{n=0, @dots{}, N}.
+##
+## @end table
+##
+## @seealso{qnconvolutionld}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X G] = qnconvolution( N, S, V, m )
+
+  if ( nargin < 3 || nargin > 4 )
+    print_usage();
+  endif
+
+  ( isscalar(N) && N>0 ) || \
+      usage( "K must be a positive scalar" );
+  K = N; # To be compliant with the reference, we use K to denote the population size
+  ( isvector(S) && all(S >= 0) ) || \
+      usage( "S must be a vector of positive scalars" );
+  S = S(:)'; # make S a row vector
+  N = length(S); # Number of service centers
+  ( isvector(V) && size_equal(V,S) ) || \
+      usage( "V must be a vector of the same length as S" );
+  V = V(:)';
+
+  if ( nargin < 4 )
+    m = ones(1,N);
+  else
+    isvector(m) || \
+	usage( "m must be a vector" );
+    m = m(:)';
+    [err m S] = common_size(m, S);
+    (err == 0) || \
+        usage( "m and S have incompatible size" );
+  endif
+
+  ## First, we remember the indexes of IS nodes
+  i_delay = find(m<1);
+
+  m( i_delay ) = K; # IS nodes are handled as if they were M/M/K nodes with number of servers equal to the population size K, such that queueing never occurs.
+
+  ## Initialization
+  G_n = G_nm1 = zeros(1,K+1); G_n(1) = 1;
+  F_n = zeros(N,K+1); F_n(:,1) = 1;
+  k=0:K; G_nm1(k+1) = F_n(1,k+1) = F(1,k,V,S,m);
+  ## Main convolution loop
+  for n=2:N
+    k=1:K; F_n(n,1+k) = F(n,k,V,S,m);
+    G_n = conv( F_n(n,:), G_nm1(:) )(1:K+1);
+    G_nm1 = G_n;
+  endfor
+  ## Done computation of G(n,k).
+  G = G_n;
+  G = G(:)'; # ensure G is a row vector
+  ## Computes performance measures
+  X = V*G(K)/G(K+1);
+  U = X .* S ./ m;
+  ## Adjust utilization of delay centers
+  U(i_delay) = X(i_delay) .* S(i_delay);
+  Q = zeros(1,N);
+  i_multi = find(m>1);
+  for i=i_multi
+    G_N_i = zeros(1,K+1);
+    G_N_i(1) = 1;
+    for k=1:K
+      j=1:k;
+      G_N_i(k+1) = G(k+1)-dot( F_n(i,j+1), G_N_i(k-j+1) );
+    endfor
+    k=0:K;
+    p_i(k+1) = F_n(i,k+1)./G(K+1).*G_N_i(K-k+1);
+    Q(i) = dot( k, p_i( k+1 ) );
+  endfor
+  i_single = find(m==1);
+  for i=i_single
+    k=1:K;
+    Q(i) = sum( ( V(i)*S(i) ) .^ k .* G(K+1-k)/G(K+1) );
+  endfor
+  R = Q ./ X;
+endfunction
+
+%!test
+%! # Example 8.1 p. 318 Bolch et al.
+%! K=3;
+%! S = [ 1/0.8 1/0.6 1/0.4 ];
+%! m = [2 3 1];
+%! V = [ 1 .667 .2 ];
+%! [U R Q X G] = qnconvolution( K, S, V, m );
+%! assert( G, [1 2.861 4.218 4.465], 5e-3 );
+%! assert( X, [0.945 0.630 0.189], 1e-3 );
+%! assert( U, [0.590 0.350 0.473], 1e-3 );
+%! assert( Q, [1.290 1.050 0.660], 1e-3 );
+%! assert( R, [1.366 1.667 3.496], 1e-3 );
+
+%!test
+%! # Example 8.3 p. 331 Bolch et al.
+%! # compare results of convolution to those of mva
+%! S = [ 0.02 0.2 0.4 0.6 ];
+%! K = 6;
+%! V = [ 1 0.4 0.2 0.1 ];
+%! [U_mva R_mva Q_mva X_mva G_mva] = qnclosedsinglemva(K, S, V);
+%! [U_con R_con Q_con X_con G_con] = qnconvolution(K, S, V);
+%! assert( U_mva, U_con, 1e-5 );
+%! assert( R_mva, R_con, 1e-5 );
+%! assert( Q_mva, Q_con, 1e-5 );
+%! assert( X_mva, X_con, 1e-5 );
+%! assert( G_mva, G_con, 1e-5 );
+
+%!test
+%! # Compare the results of convolution to those of mva
+%! S = [ 0.02 0.2 0.4 0.6 ];
+%! K = 6;
+%! V = [ 1 0.4 0.2 0.1 ];
+%! m = [ 1 -1 2 1 ]; # center 2 is IS
+%! [U_mva R_mva Q_mva X_mva] = qnclosedsinglemva(K, S, V, m);
+%! [U_con R_con Q_con X_con G] = qnconvolution(K, S, V, m );
+%! assert( U_mva, U_con, 1e-5 );
+%! assert( R_mva, R_con, 1e-5 );
+%! assert( Q_mva, Q_con, 1e-5 );
+%! assert( X_mva, X_con, 1e-5 );
+
+## result = F(i,j,v,S,m)
+##
+## Helper fuction to compute F(i,j) as defined in Eq 7.61 p. 289 of
+## Bolch, Greiner, de Meer, Trivedi "Queueing Networks and Markov
+## Chains: Modeling and Performance Evaluation with Computer Science
+## Applications", Wiley, 1998. This function has been vectorized,
+## and accepts a vector as parameter j.
+function result = F(i,j,v,S,m)
+  isscalar(i) || \
+      usage( "i must be a scalar" );
+  k_i = j;
+  if ( m(i) == 1 )
+    result = ( v(i)*S(i) ).^k_i;
+  else
+    ii = find(k_i<=m(i)); ## if k_i<=m(i)
+    result(ii) = ( v(i)*S(i) ).^k_i(ii) ./ factorial(k_i(ii));
+    ii = find(k_i>m(i)); ## if k_i>m(i)
+    result(ii) = ( v(i)*S(i) ).^k_i(ii) ./ ( factorial(m(i))*m(i).^(k_i(ii)-m(i)) );
+  endif
+endfunction
+
+%!demo
+%! k = [1 2 0];
+%! K = sum(k); # Total population size
+%! S = [ 1/0.8 1/0.6 1/0.4 ];
+%! m = [ 2 3 1 ];
+%! V = [ 1 .667 .2 ];
+%! [U R Q X G] = qnconvolution( K, S, V, m );
+%! p = [0 0 0]; # initialize p
+%! # Compute the probability to have k(i) jobs at service center i
+%! for i=1:3
+%!   p(i) = (V(i)*S(i))^k(i) / G(K+1) * \
+%!          (G(K-k(i)+1) - V(i)*S(i)*G(K-k(i)) );
+%!   printf("k(%d)=%d prob=%f\n", i, k(i), p(i) );
+%! endfor
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnconvolutionld.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,220 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qnconvolutionld (@var{N}, @var{S}, @var{V})
+##
+## @cindex closed network
+## @cindex normalization constant
+## @cindex convolution algorithm
+## @cindex load-dependent service center
+##
+## This function implements the @emph{convolution algorithm} for
+## product-form, single-class closed queueing networks with general
+## load-dependent service centers.
+##
+## This function computes steady-state performance measures for
+## single-class, closed networks with load-dependent service centers
+## using the convolution algorithm; the normalization constants are also
+## computed. The normalization constants are returned as vector
+## @code{@var{G}=[@var{G}(1), @dots{}, @var{G}(N+1)]} where
+## @code{@var{G}(i+1)} is the value of @math{G(i)}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## Number of requests in the system (@code{@var{N}>0}).
+##
+## @item S
+## @code{@var{S}(k,n)} is the mean service time at center @math{k}
+## where there are @math{n} requests, @math{1 @leq{} n
+## @leq{} N}. @code{@var{S}(k,n)} @math{= 1 / \mu_{k,n}},
+## where @math{\mu_{k,n}} is the service rate of center @math{k}
+## when there are @math{n} requests.
+##
+## @item V
+## @code{@var{V}(k)} is the visit count of service center @math{k}
+## (@code{@var{V}(k) @geq{} 0}). The length of @var{V} is the number of
+## servers @math{K} in the network.
+##
+## @end table
+##
+## @strong{OUTPUT}
+##
+## @table @var
+##
+## @item U
+## @code{@var{U}(k)} is the utilization of center @math{k}.
+##
+## @item R
+## @code{@var{R}(k)} is the average response time at center @math{k}.
+##
+## @item Q
+## @code{@var{Q}(k)} is the average number of customers in center @math{k}.
+##
+## @item X
+## @code{@var{X}(k)} is the throughput of center @math{k}.
+##
+## @item G
+## Normalization constants (vector). @code{@var{G}(n+1)}
+## corresponds to @math{G(n)}, as array indexes in Octave start
+## from 1.
+##
+## @end table
+##
+## @seealso{qnconvolution}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X G] = qnconvolutionld( N, S, V )
+
+  if ( nargin != 3 )
+    print_usage();
+  endif
+
+  ( isscalar(N) && N>0 ) || \
+      usage( "N must be a positive scalar" );
+  K = N; # To be compliant with the reference, we denote K as the population size
+  ( isvector(V) ) || \
+      usage( "V must be a vector" );
+  V = V(:)'; # Make V a row vector
+  N = length(V); # Number of service centers
+  if ( isnumeric(S) ) 
+    ( rows(S) == N && columns(S) == K) || \
+        usage( sprintf("S size mismatch: is %dx%d, should be %dx%d", rows(S), columns(S),K,N ) );
+    all( all(S>=0) ) || \
+        usage( "S must be >=0" );
+  endif
+
+  ## Initialization
+  G_n = G_nm1 = zeros(1,K+1); G_n(1) = 1;
+  F_n = zeros(N,K+1); F_n(:,1) = 1;
+  for k=0:K
+    G_nm1(k+1) = F_n(1,k+1) = F(1,k,V,S);
+  endfor
+  ## Main convolution loop
+  for n=2:N
+    for k=2:K+1
+      F_n(n,k) = F(n,k-1,V,S);
+    endfor
+    G_n = conv( F_n(n,:), G_nm1(:) )(1:K+1);
+    G_nm1 = G_n;
+  endfor
+  ## Done computation of G(n,k).
+  G = G_n;
+  G = G(:)'; # ensure G is a row vector
+  ## Computes performance measures
+  X = V*G(K)/G(K+1);
+  Q = U = zeros(1,N);
+  for i=1:N
+    G_N_i = zeros(1,K+1);
+    G_N_i(1) = 1;
+    for k=1:K
+      j=1:k;
+      G_N_i(k+1) = G(k+1)-dot( F_n(i,j+1), G_N_i(k-j+1) );
+    endfor
+    k=0:K;
+    p_i(k+1) = F_n(i,k+1)./G(K+1).*G_N_i(K-k+1);
+    Q(i) = dot( k, p_i( k+1 ) );
+    U(i) = 1-p_i(1);
+  endfor
+  R = Q ./ X;
+endfunction
+%!test
+%! K=3;
+%! S = [ 1 1 1; 1 1 1 ];
+%! V = [ 1 .667 .2 ];
+%! fail( "qnconvolutionld(K,S,V)", "size mismatch" );
+
+%!test
+%! # Example 8.1 p. 318 Bolch et al.
+%! K=3;
+%! S = [ 1/0.8 ./ [1 2 2];
+%!       1/0.6 ./ [1 2 3];
+%!       1/0.4 ./ [1 1 1] ];
+%! V = [ 1 .667 .2 ];
+%! [U R Q X G] = qnconvolutionld( K, S, V );
+%! assert( G, [1 2.861 4.218 4.465], 5e-3 );
+%! assert( X, [0.945 0.630 0.189], 1e-3 );
+%! assert( Q, [1.290 1.050 0.660], 1e-3 );
+%! assert( R, [1.366 1.667 3.496], 1e-3 );
+
+%!test
+%! # Example 8.3 p. 331 Bolch et al.
+%! # compare results of convolution with those of mva
+%! K = 6;
+%! S = [ 0.02 0.2 0.4 0.6 ];
+%! V = [ 1 0.4 0.2 0.1 ];
+%! [U_mva R_mva Q_mva X_mva] = qnclosedsinglemva(K, S, V);
+%! [U_con R_con Q_con X_con G] = qnconvolutionld(K, repmat(S',1,K), V );
+%! assert( U_mva, U_con, 1e-5 );
+%! assert( R_mva, R_con, 1e-5 );
+%! assert( Q_mva, Q_con, 1e-5 );
+%! assert( X_mva, X_con, 1e-5 );
+
+%!test
+%! # Compare the results of convolution to those of mva
+%! S = [ 0.02 0.2 0.4 0.6 ];
+%! K = 6;
+%! V = [ 1 0.4 0.2 0.1 ];
+%! m = [ 1 5 2 1 ];
+%! [U_mva R_mva Q_mva X_mva] = qnclosedsinglemva(K, S, V);
+%! [U_con R_con Q_con X_con G] = qnconvolutionld(K, repmat(S',1,K), V);
+%! assert( U_mva, U_con, 1e-5 );
+%! assert( R_mva, R_con, 1e-5 );
+%! assert( Q_mva, Q_con, 1e-5 );
+%! assert( X_mva, X_con, 1e-5 );
+
+%!function r = S_function(k,n)
+%! M = [ 1/0.8 ./ [1 2 2];
+%!       1/0.6 ./ [1 2 3];
+%!       1/0.4 ./ [1 1 1] ];
+%! r = M(k,n);
+
+%!test
+%! # Example 8.1 p. 318 Bolch et al.
+%! K=3;
+%! V = [ 1 .667 .2 ];
+%! [U R Q X G] = qnconvolutionld( K, @S_function, V );
+%! assert( G, [1 2.861 4.218 4.465], 5e-3 );
+%! assert( X, [0.945 0.630 0.189], 1e-3 );
+%! assert( Q, [1.290 1.050 0.660], 1e-3 );
+%! assert( R, [1.366 1.667 3.496], 1e-3 );
+
+## result = F(i,j,v,S)
+##
+## Helper fuction to compute a generalization of equation F(i,j) as
+## defined in Eq 7.61 p. 289 of Bolch, Greiner, de Meer, Trivedi
+## "Queueing Networks and Markov Chains: Modeling and Performance
+## Evaluation with Computer Science Applications", Wiley, 1998. This
+## generalization is taken from Schwetman, "Some Computational Aspects
+## of Queueing Network Models", Technical Report CSD-TR 354, Dept. of
+## CS, Purdue University, Dec 1980 (see definition of f_i(n) on p. 7).
+function result = F(i,j,v,S)
+  k_i = j;
+  if ( k_i == 0 )
+    result = 1;
+  else
+    result = v(i)^k_i * prod(S(i,1:k_i));
+  endif
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnjackson.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,223 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnjackson (@var{lambda}, @var{S}, @var{P} )
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnjackson (@var{lambda}, @var{S}, @var{P}, @var{m} )
+## @deftypefnx {Function File} {@var{pr} =} qnjackson (@var{lambda}, @var{S}, @var{P}, @var{m}, @var{k})
+##
+## @cindex open network, single class
+## @cindex Jackson network
+##
+## With three or four input parameters, this function computes the
+## steady-state occupancy probabilities for a Jackson network. With five
+## input parameters, this function computes the steady-state probability
+## @code{@var{pi}(j)} that there are @code{@var{k}(j)} requests at
+## service center @math{j}.
+##
+## This function solves a subset of Jackson networks, with the
+## following constraints:
+##
+## @itemize
+##
+## @item External arrival rates are load-independent.
+## 
+## @item Service center @math{i} consists either of @code{@var{m}(i) @geq{}
+## 1} identical servers with individual average service time
+## @code{@var{S}(i)}, or of an Infinite Server (IS) node.
+##
+## @end itemize
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## @code{@var{lambda}(i)} is
+## the external arrival rate to service center @math{i}. @var{lambda}
+## must be a vector of length @math{N}, @code{@var{lambda}(i) @geq{} 0}.
+##
+## @item S
+## @code{@var{S}(i)} is the average service time on service center @math{i}
+## @var{S} must be a vector of length @math{N}, @code{@var{S}(i)>0}.
+##
+## @item P
+## @code{@var{P}(i,j)} is the probability
+## that a job which completes service at service center @math{i} proceeds
+## to service center @math{j}. @var{P} must be a matrix of size
+## @math{N \times N}.
+##
+## @item m
+## @code{@var{m}(i)} is the number of servers at service center
+## @math{i}. If @code{@var{m}(i) < 1}, service center @math{i} is an
+## infinite-server node. Otherwise, it is a regular FCFS queueing center with
+## @code{@var{m}(i)} servers. If this parameter is omitted, default is
+## @code{@var{m}(i) = 1} for all @math{i}. If this parameter is a scalar,
+## it will be promoted to a vector with the same size as @var{lambda}.
+## Otherwise, @var{m} must be a vector of length @math{N}.
+##
+## @item k
+## Compute the steady-state probability that there are @code{@var{k}(i)}
+## requests at service center @math{i}. @var{k} must have the same length
+## as @var{lambda}, with @code{@var{k}(i) @geq{} 0}.
+## 
+## @end table
+##
+## @strong{OUTPUT}
+##
+## @table @var
+##
+## @item U
+## If @math{i} is a FCFS node, then
+## @code{@var{U}(i)} is the utilization of service center @math{i}.
+## If @math{i} is an IS node, then @code{@var{U}(i)} is the
+## @emph{traffic intensity} defined as @code{@var{X}(i)*@var{S}(i)}.
+##
+## @item R
+## @code{@var{R}(i)} is the average response time of service center @math{i}.
+##
+## @item Q
+## @code{@var{Q}(i)} is the average number of customers in service center
+## @math{i}.
+##
+## @item X
+## @code{@var{X}(i)} is the throughput of service center @math{i}.
+##
+## @item pr
+## @code{@var{pr}(i)} is the steady state probability 
+## that there are @code{@var{k}(i)} requests at service center @math{i}.
+##
+## @end table
+##
+## @seealso{qnopen}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U_or_pi R Q X] = qnjackson( lambda, S, P, m, k )
+  if ( nargin < 3 || nargin > 5 )
+    print_usage();
+  endif
+  ( isvector(lambda) && all(lambda>=0) ) || \
+      usage( "lambda must be a vector >= 0" );
+  lambda=lambda(:)'; # make lambda a row vector
+  N = length(lambda);
+  isvector(S) || \
+      usage( "S must be a vector" );
+  S = S(:)'; # make S a row vector
+  size_equal(lambda,S) || \
+      usage( "lambda and S must of be of the same length" );
+  all(S>0) || \
+      usage( "S must be >0" );
+  [N,N] == size(P) || \
+      usage(" P must be a matrix of size length(lambda) x length(lambda)" );  
+  all(all(P>=0)) && all(sum(P,2)<=1) || \
+      error( "P is not a transition probability matrix" );
+
+  if ( nargin < 4 )
+    m = ones(1,N);
+  else
+    [errorcode, lambda, m] = common_size(lambda, m);
+    ( isvector(m) && (errorcode==0) ) || \
+        usage("m and lambda must have the same length" );
+  endif
+
+  ## Compute the arrival rates using the traffic equation:
+  l = sum(lambda)*qnvisits( P, lambda );
+  ## Check ergodicity
+  for i=1:N
+    if ( m(i)>0 && l(i)>=m(i)/S(i) )      
+      error( "Server %d not ergodic: arrival rate=%f, service rate=%f", i, l(i), m(i)/S(i) );
+    endif
+  endfor
+
+  U_or_pi = zeros(1,N);
+
+  if ( nargin == 5 )
+
+    ( isvector(k) && size_equal(lambda,k) ) || \
+        usage( "k must be a vector of the same size as lambda" );
+    all(k>=0) || \
+        usage( "k must be nonnegative" );
+
+    ## compute occupancy probability
+    rho = l .* S ./ m;      
+    i = find(m==1); # M/M/1 queues
+    U_or_pi(i) = (1-rho(i)).*rho(i).^k(i);
+    for i=find(m>1) # M/M/k queues
+      k = [0:m(i)-1];
+      pizero = 1 / (sum( (m(i)*rho(i)).^k ./ factorial(k)) + \
+                    (m(i)*rho(i))^m(i) / (factorial(m(i))*(1-rho(i))) \
+                    );      
+      ## Compute the marginal probabilities
+      U_or_pi(i) = pizero * (m(i)^min(k(i),m(i))) * (rho(i)^k(i)) / \
+          factorial(min(m(i),k(i)));
+    endfor
+    i = find(m<1); # infinite server nodes
+    U_or_pi(i) = exp(-rho(i)).*rho(i).^k(i)./factorial(k(i));
+
+  else 
+
+    ## Compute steady-state parameters
+    U_or_pi = R = Q = X = zeros(1,N); # Initialize vectors
+    ## single server nodes
+    i = find( m==1 );
+    [U_or_pi(i) R(i) Q(i) X(i)] = qnmm1(l(i),1./S(i));
+    ## multi server nodes  
+    i = find( m>1 );
+    [U_or_pi(i) R(i) Q(i) X(i)] = qnmmm(l(i),1./S(i),m(i));
+    ## infinite server nodes
+    i = find( m<1 );
+    [U_or_pi(i) R(i) Q(i) X(i)] = qnmminf(l(i),1./S(i));
+
+  endif
+endfunction
+%!test
+%! # Test various error conditions
+%! fail( "qnjackson( [0.5 0.5], [0 1], [0 0; 0 0], [1 1])", "S must be" );
+%! fail( "qnjackson( [-1 1], [1 1], [0 0; 0 0], [1 1])", "lambda must be" );
+%! fail( "qnjackson( [0.5 0.5], [1 1], [1 1; 0 0], [1 1])", "P is not" );
+%! fail( "qnjackson( [0.5 0.5], [1 1], [1 0; -1 0], [1 1])", "P is not" );
+%! fail( "qnjackson( [0.5 0.5], [1 1 1], [0 0; 0 0], [1 1])", "lambda and S" );
+%! fail( "qnjackson( [0.5 0.5], [1 1], [0 0; 0 0], [1 1 1])", "m and lambda" );
+%! fail( "qnjackson( [0.5 0.5], [1 1], [0 0; 0 0], [1 1], [1 1 1])", "k must be" );
+%! fail( "qnjackson( [0.5 0.5], [1 1], [0 0; 0 0], [1 1], [1 -1])", "k must be" );
+%! fail( "qnjackson( [0 1], [2 2], [0 0.9; 0 0] )", "not ergodic" );
+
+%!test
+%! # Example 7.4 p. 287 Bolch et al.
+%! S = [ 0.04 0.03 0.06 0.05 ];
+%! P = [ 0 0.5 0.5 0; 1 0 0 0; 0.6 0 0 0; 1 0 0 0 ];
+%! lambda = [0 0 0 4];
+%! k = [ 3 2 4 1 ];
+%! [U R Q X] = qnjackson( lambda, S, P, 1 );
+%! assert( X, [20 10 10 4], 1e-4 );
+%! assert( U, [0.8 0.3 0.6 0.2], 1e-2 );
+%! assert( R, [0.2 0.043 0.15 0.0625], 1e-3 );
+%! assert( Q, [4, 0.429 1.5 0.25], 1e-3 );
+
+%!test
+%! # Example 7.4 p. 287 Bolch et al.
+%! S = [ 0.04 0.03 0.06 0.05 ];
+%! P = [ 0 0.5 0.5 0; 1 0 0 0; 0.6 0 0 0; 1 0 0 0 ];
+%! lambda = [0 0 0 4];
+%! k = [ 3 2 4 1 ];
+%! p_i = qnjackson( lambda, S, P, 1, k );
+%! assert( p_i, [0.1024, 0.063, 0.0518, 0.16], 1e-4 );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmarkov.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,344 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmarkov (@var{lambda}, @var{S}, @var{C}, @var{P})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmarkov (@var{lambda}, @var{S}, @var{C}, @var{P}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmarkov (@var{N}, @var{S}, @var{C}, @var{P})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmarkov (@var{N}, @var{S}, @var{C}, @var{P}, @var{m})
+##
+## @cindex closed network, multiple classes
+## @cindex closed network, finite capacity
+## @cindex blocking queueing network
+## @cindex RS blocking
+##
+## Compute utilization, response time, average queue length and
+## throughput for open or closed queueing networks with finite capacity.
+## Blocking type is Repetitive-Service (RS). This function explicitly
+## generates and solve the underlying Markov chain, and thus might
+## require a large amount of memory.
+## 
+## More specifically, networks which can me analyzed by this
+## function have the following properties:
+##
+## @itemize @bullet
+##
+## @item There exists only a single class of customers.
+##
+## @item The network has @math{K} service centers. Center
+## @math{i} has @math{m_i > 0} servers, and has a total (finite) capacity of
+## @math{C_i \geq m_i} which includes both buffer space and servers.
+## The buffer space at service center @math{i} is therefore
+## @math{C_i - m_i}.
+##
+## @item The network can be open, with external arrival rate to
+## center @math{i} equal to 
+## @math{\lambda_i}, or closed with fixed
+## population size @math{N}. For closed networks, the population size
+## @math{N} must be strictly less than the network capacity: @math{N < \sum_i C_i}.
+##
+## @item Average service times are load-independent.
+##
+## @item @math{P_{ij}} is the probability that requests completing
+## execution at center @math{i} are transferred to
+## center @math{j}, @math{i \neq j}. For open networks, a request may leave the system
+## from any node @math{i} with probability @math{1-\sum_j P_{ij}}.
+##
+## @item Blocking type is Repetitive-Service (RS). Service
+## center @math{j} is @emph{saturated} if the number of requests is equal
+## to its capacity @code{C_j}. Under the RS blocking discipline,
+## a request completing service at center @math{i} which is being
+## transferred to a saturated server @math{j} is put back at the end of
+## the queue of @math{i} and will receive service again. Center @math{i}
+## then processes the next request in queue. External arrivals to a
+## saturated servers are dropped.
+##
+## @end itemize
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## @itemx N
+## If the first argument is a vector @var{lambda}, it is considered to be
+## the external arrival rate @code{@var{lambda}(i) @geq{} 0} to service center
+## @math{i} of an open network. If the first argument is a scalar, it is
+## considered as the population size @var{N} of a closed network; in this case
+## @var{N} must be strictly
+## less than the network capacity: @code{@var{N} < sum(@var{C})}.
+##
+## @item S
+## @code{@var{S}(i)} is the average service time at service center
+## @math{i}
+##
+## @item C
+## @code{@var{C}(i)} is the Capacity of service center @math{i}. The capacity includes both
+## the buffer and server space @code{@var{m}(i)}. Thus the buffer space is
+## @code{@var{C}(i)-@var{m}(i)}.
+##
+## @item P
+## @code{@var{P}(i,j)} is the transition probability from service center
+## @math{i} to service center @math{j}.
+##
+## @item m
+## @code{@var{m}(i)} is the number of servers at service center
+## @math{i}. Note that @code{@var{m}(i) @geq{} @var{C}(i)} for each @var{i}.
+## If @var{m} is omitted, all service centers are assumed to have a
+## single server (@code{@var{m}(i) = 1} for all @math{i}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## @code{@var{U}(i)} is the utilization of service center @math{i}.
+##
+## @item R
+## @code{@var{R}(i)} is the response time on service center @math{i}.
+##
+## @item Q
+## @code{@var{Q}(i)} is the average number of customers in the
+## service center @math{i}, @emph{including} the request in service.
+##
+## @item X
+## @code{@var{X}(i)} is the throughput of service center @math{i}.
+##
+## @end table
+##
+## @quotation Note
+##
+## The space complexity of this implementation is
+## @math{O( \prod_{i=1}^K (C_i + 1)^2)}. The time complexity is dominated
+## by the time needed to solve a linear system with 
+## @math{\prod_{i=1}^K (C_i + 1)}
+## unknowns.
+##
+## @end quotation
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnmarkov( x, S, C, P, m )
+
+  if ( nargin < 4 || nargin > 5 )
+    print_usage();
+  endif
+
+  isvector(S) || usage( "S must be a vector" );
+  K = length(S); # number of service centers
+
+  if ( nargin < 5 )
+    m = ones(1,K);
+  else
+    size_equal(m,S) || usage( "m must have the same langth as S" );
+  endif
+
+  ( [K,K] == size(P) && all( all(P>=0)) && all(sum(P,2) <= 1)) || \
+      usage( "P must be SxS and nonnegative" );
+
+  if ( isscalar(x) )
+    is_open = false;
+    N = x; # closed network
+    ( N < sum(C) ) || \
+        error( "The population size exceeds the network capacity" );
+    all( abs(sum(P,2)-1) < 1000*eps ) || \
+        error( "P for closed networks cannot have exit nodes" );
+  else
+    is_open = true;
+    lambda = x; # open network
+    size_equal(lambda, S ) || \
+        usage( "lambda must have the same langth as S" );
+  endif
+
+  ( all(m > 0) && all(m <= C) ) || \
+      error( "Capacities C must be greater or equal than m" );
+
+  q_size = prod( C+1 ); # number of states of the system
+
+  ## The infinitesimal generator matrix Q_markovmight be sparse, so it
+  ## would be appropriate to represent it as a sparse matrix. In this
+  ## case the UMFPACK library must be installed to solve the @math{{\bf
+  ## A}x=b} sparse system. Since I'm not sure everyone built Octave with
+  ## sparse matrix support, I leave Q_markov as a full matrix here.
+  Q_markov = zeros( q_size, q_size );
+  cur_state = zeros(1, K);
+
+  if ( is_open ) 
+    valid_populations = linspace(0, sum(C), sum(C)+1);
+  else
+    valid_populations = [ N ];
+  endif
+  ## exit_prob(i) is the probability that a job leaves the system from node i
+  exit_prob = 1 - sum(P,2);
+  for n=valid_populations
+    pop_mix = population_mix( n, C );
+    for cur_state=pop_mix' # for each feasible configuration with n customers
+      cur_idx = sub2cell( C, cur_state );
+      for i=1:K
+        if ( is_open ) # for open networks only
+          ## handle new external arrival to center i
+          if ( lambda(i) > 0 && cur_state(i) < C(i) )
+            next_state = cur_state; next_state(i) += 1;
+            next_idx = sub2cell( C, next_state );
+            Q_markov( cur_idx, next_idx ) = lambda(i);
+          endif
+          ## handle requests that leave the system from center i
+          ## exit_prob = 1-sum(P(i,:));
+          if ( exit_prob(i) > 0 && cur_state(i) > 0 )
+            next_state = cur_state; next_state(i) -= 1;
+            next_idx = sub2cell( C, next_state );
+            Q_markov( cur_idx, next_idx ) = min(m(i), cur_state(i))*exit_prob(i)/S(i);            
+          endif
+        endif # end open networks only
+        ## for both open and closed networks
+        for j=[1:(i-1) (i+1):K]
+          ## check whether a job can move from server i to server j!=i
+          if ( cur_state(i) > 0 && cur_state(j) < C(j) && P(i,j) > 0 )
+            next_state = cur_state; next_state(i) -= 1; next_state(j) += 1;
+            next_idx = sub2cell( C, next_state );
+            Q_markov( cur_idx, next_idx ) = min(m(i), cur_state(i))*P(i,j)/S(i);
+          endif
+        endfor
+      endfor
+    endfor
+  endfor
+  ##spy( Q_markov );
+  ## complete the diagonal elements of the matrix Q
+  d = sum(Q_markov,2);
+  Q_markov -= diag( d );
+  ## Solve the ctmc
+  prob = ctmc( Q_markov );
+  ## Compute the average queue length
+  p = zeros(K, max(C)+1); # p(k,i+1) = prob that there are i requests at service center k
+  for n=valid_populations
+    pop_mix = population_mix( n, C );
+    for cur_state=pop_mix'
+      cur_idx = sub2cell( C, cur_state );
+      for k=1:K
+        i=cur_state(k);
+        p(k,i+1) += prob(cur_idx);
+      endfor
+    endfor
+  endfor
+  ## We can now compute all the performance measures
+  U = R = Q = X = zeros(1,K);
+  for k=1:K
+    j = [0:m(k)-1];
+    U(k) = 1 - sum( ( m(k) - j ) ./ m(k) .* p(k,1+j) );
+    ##X(k) = U(k)/S(k);
+    j = [1:C(k)];
+    Q(k) = sum( j .* p(k,1+j) );
+    ##R(k) = Q(k)/X(k);
+  endfor
+  X = U./S;
+  R = Q./X;
+endfunction
+%!test
+%! S = [5 2.5];
+%! P = [0 1; 1 0];
+%! C = [3 3];
+%! m = [1 1];
+%! [U R Q X] = qnmarkov( 3, S, C, P, m );
+%! assert( U, [0.9333 0.4667], 1e-4 );
+%! assert( X, [0.1867 0.1867], 1e-4 );
+%! assert( R, [12.1429 3.9286], 1e-4 );
+
+## Example 7.5 p. 292 Bolch et al.
+%!test
+%! S = [1/0.8 1/0.6 1/0.4];
+%! P = [0.6 0.3 0.1; 0.2 0.3 0.5; 0.4 0.1 0.5];
+%! C = [3 3 3];
+%! [U R Q X] = qnmarkov( 3, S, C, P );
+%! assert( U, [0.543 0.386 0.797], 1e-3 );
+%! assert( Q, [0.873 0.541 1.585], 1e-3 );
+
+## Example 10.19, p. 551 Bolch et al.
+%!xtest
+%! S = [2 0.9];
+%! C = [7 5];
+%! P = [0 1; 1 0];
+%! [U R Q X] = qnmarkov( 10, S, C, P );
+%! assert( Q, [6.73 3.27], 1e-3 );
+
+## Example 8.1 p. 317 Bolch et al.
+%!test
+%! S = [1/0.8 1/0.6 1/0.4];
+%! P = [(1-0.667-0.2) 0.667 0.2; 1 0 0; 1 0 0];
+%! m = [2 3 1];
+%! C = [3 3 3];
+%! [U R Q X] = qnmarkov( 3, S, C, P, m );
+%! assert( U, [0.590 0.350 0.473], 1e-3 );
+%! assert( Q(1:2), [1.290 1.050], 1e-3 );
+
+## This is a simple test of an open QN with fixed capacity queues. There
+## are two service centers, S1 and S2. C(1) = 2 and C(2) = 1. Transition
+## probability from S1 to S2 is 1. Transition probability from S2 to S1
+## is p.
+%!test
+%! p = 0.5; # transition prob. from S2 to S1
+%! mu = [1 2]; # Service rates
+%! C = [2 1]; # Capacities
+%! lambda = [0.5 0]; # arrival rate at service center 1
+%!
+%! PP = [ 0 1; p 0 ];
+%! [U R Q X] = qnmarkov( lambda, 1./mu, C, PP );
+%! ## Now we generate explicitly the infinitesimal generator matrix
+%! ## of the underlying MC.
+%! ##    00  01  10  11  20  21
+%! QQ = [ 0 0 lambda(1) 0 0 0; ... ## 00
+%!        mu(2)*(1-p) 0 mu(2)*p lambda(1) 0 0; ... ## 01
+%!        0 mu(1) 0 0 lambda(1) 0; ... ## 10
+%!        0 0 mu(2)*(1-p) 0 mu(2)*p lambda(1); ... ## 11
+%!        0 0 0 mu(1) 0 0; ... ## 20
+%!        0 0 0 0 mu(2)*(1-p) 0 ]; ## 21
+%! ## Complete matrix
+%! sum_el = sum(QQ,2);
+%! QQ -= diag(sum_el);
+%! q = ctmc(QQ);
+%! ## Compare results
+%! assert( U(1), 1-sum(q([1, 2])), 1e-5 );
+%! assert( U(2), 1-sum(q([1,3,5])), 1e-5 );
+
+## This is a closed network with fixed-capacity queues. The population
+## size N is such that blocking never occurs, so this model can be
+## analyzed using the conventional MVA algorithm. MVA and qnmarkov()
+## must produce the same results.
+%!test
+%! P = [0 0.5 0.5; 1 0 0; 1 0 0];
+%! C = [6 6 6];
+%! S = [1 0.8 1.8];
+%! N = 6;
+%! [U1 R1 Q1 X1] = qnclosed( N, S, qnvisits(P) );
+%! [U2 R2 Q2 X2] = qnmarkov( N, S, C, P );
+%! assert( U1, U2, 1e-6 );
+%! assert( R1, R2, 1e-6 );
+%! assert( Q1, Q2, 1e-6 );
+%! assert( X1, X2, 1e-6 );
+
+## return a linear index corresponding to index idx on a
+## multidimensional vector of dimension(s) dim
+function i = sub2cell( dim, idx )
+  idx_cell = num2cell( idx+1 );
+  i = sub2ind( dim+1, idx_cell{:} );
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmg1.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,100 @@
+## Copyright (C) 2009 Dmitry Kolesnikov
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}] =} qnmg1 (@var{lambda}, @var{xavg}, @var{x2nd})
+##
+## @cindex @math{M/G/1} system
+##
+## Compute utilization, response time, average number of requests and
+## throughput for a @math{M/G/1} system. The service time distribution
+## is described by its mean @var{xavg}, and by its second moment
+## @var{x2nd}. The computations are based on results from L. Kleinrock,
+## @cite{Queuing Systems}, Wiley, Vol 2, and Pollaczek-Khinchine formula.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate.
+##
+## @item xavg
+## Average service time
+##
+## @item x2nd
+## Second moment of service time distribution
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## Service center utilization
+##
+## @item R
+## Service center response time
+##
+## @item Q
+## Average number of requests in the system
+##
+## @item X
+## Service center throughput
+##
+## @item p0
+## probability that there is not any request at system
+##
+## @end table
+##
+## @var{lambda}, @var{xavg}, @var{t2nd} can be vectors of the
+## same size. In this case, the results will be vectors as well.
+##
+## @seealso{qnmh1}
+##
+## @end deftypefn
+
+## Author: Dmitry Kolesnikov
+
+function [U R Q X p0] = qnmg1(lambda, xavg, x2nd)
+   if ( nargin != 3 )
+      print_usage();
+   endif
+   ## bring the parameters to a common size
+   [ err lambda xavg x2nd ] = common_size( lambda, xavg, x2nd );
+   if ( err ) 
+      usage( "parameters are of incompatible size" );
+   endif
+
+   mu = 1 ./ xavg;
+   rho = lambda ./ mu; 
+
+   #coefficient of variation
+   Cx = (x2nd .- xavg .* xavg) ./ (xavg .* xavg);
+
+   #PK mean formula(s)
+   Q = rho  .+ rho .* rho .* (1 .+ Cx) ./ (2 .* (1 .- rho));
+   R = xavg .+ xavg .* rho .* (1 .+ Cx) ./ (2 .* (1 .- rho));
+
+   p0 = exp(-rho);
+   #General Results
+   #utilization
+   U = rho; 
+   X = lambda;
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmh1.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,117 @@
+## Copyright (C) 2009 Dmitry Kolesnikov
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}] =} qnmh1 (@var{lambda}, @var{mu}, @var{alpha})
+##
+## @cindex @math{M/H_m/1} system
+##
+## Compute utilization, response time, average number of requests and
+## throughput for a @math{M/H_m/1} system. In this system, the customer
+## service times have hyper-exponential distribution:
+##
+## @iftex
+## @tex
+## $$ B(x) = \sum_{j=1}^m \alpha_j(1-e^{-\mu_j x}),\quad x>0 $$
+## @end tex
+## @end iftex
+##
+## @ifnottex
+## @example
+## @group
+##        ___ m
+##        \
+## B(x) =  >  alpha(j) * (1-exp(-mu(j)*x))   x>0
+##        /__ 
+##            j=1
+## @end group
+## @end example
+## @end ifnottex
+##
+## where @math{\alpha_j} is the probability that the request is served
+## at phase @math{j}, in which case the average service rate is
+## @math{\mu_j}. After completing service at phase @math{j}, for
+## some @math{j}, the request exits the system.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate.
+##
+## @item mu
+## @code{@var{mu}(j)} is the phase @math{j} service rate. The total
+## number of phases @math{m} is @code{length(@var{mu})}.
+##
+## @item alpha
+## @code{@var{alpha}(j)} is the probability that a request
+## is served at phase @math{j}. @var{alpha} must have the same size
+## as @var{mu}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## Service center utilization
+##
+## @item R
+## Service center response time
+##
+## @item Q
+## Average number of requests in the system
+##
+## @item X
+## Service center throughput
+##
+## @end table
+##
+## @c @seealso{qnmhr1}
+##
+## @end deftypefn
+
+## Author: Dmitry Kolesnikov
+
+function [U R Q X p0] = qnmh1(lambda, mu, alpha)
+   if ( nargin != 3 )
+      print_usage();
+   endif
+   if ( size(mu) != size(alpha) )
+      usage( "parameters are of incompatible size" );
+   endif
+   [n c] = size(mu);
+
+   if (!is_scalar(lambda) && (n != length(lambda)) ) 
+      usage( "parameters are of incompatible size" );
+   endif
+   for i=1:n
+      avg  = sum( alpha(i,:) .* (1 ./ mu(i,:)) );
+      m2nd = sum( alpha(i,:) .* (1 ./ (mu(i,:) .* mu(i,:))) );
+      if (is_scalar(lambda))
+         xavg = avg;
+         x2nd = m2nd;  
+      else
+         xavg(i) = avg;
+         x2nd(i) = m2nd;
+      endif
+   endfor
+   [U R Q X p0] = qnmg1(lambda, xavg, x2nd);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmix.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,247 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmix (@var{lambda}, @var{N}, @var{S}, @var{V}, @var{m})
+##
+## @cindex Mean Value Analysys (MVA)
+## @cindex mixed network
+##
+## Solution of mixed queueing networks through MVA. The network consists
+## of @math{K} service centers (single-server or delay centers) and
+## @math{C} independent customer chains. Both open and closed chains
+## are possible. @var{lambda} is the vector of per-chain
+## arrival rates (open classes); @var{N} is the vector of populations
+## for closed chains.
+##
+## @quotation Note
+## In this implementation class switching is @strong{not} allowed. Each
+## customer class @emph{must} correspond to an independent chain.
+## @end quotation
+##
+## If the network is made of open or closed classes only, then this
+## function calls @code{qnopenmulti} or @code{qnclosedmultimva}
+## respectively, and prints a warning message.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## @itemx N
+## For each customer chain @math{c}:
+##
+## @itemize
+##
+## @item if @math{c} is a closed chain, then @code{@var{N}(c)>0} is the
+## number of class @math{c} requests and @code{@var{lambda}(c)} must be
+## zero;
+##
+## @item If @math{c} is an open chain,
+## @code{@var{lambda}(c)>0} is the arrival rate of class @math{c}
+## requests and @code{@var{N}(c)} must be zero;
+##
+## @end itemize
+##
+## @noindent For each @math{c}, the following must hold:
+##
+## @example
+## (@var{lambda}(c)>0 && @var{N}(c)==0) || (@var{lambda}(c)==0 && @var{N}(c)>0)
+## @end example
+##
+## which means that either @code{@var{lambda}(c)} is nonzero and
+## @code{@var{N}(n)} is zero, or the other way around. If for some
+## @math{c}, @math{@var{lambda}(c) \neq 0} and @math{@var{N}(c) \neq 0}, an
+## error is reported and this function aborts.
+##
+## @item S
+## @code{@var{S}(c,k)} is the mean service time for class @math{c}
+## customers on service center @math{k}, @code{@var{S}(c,k) @geq{} 0}.
+## For FCFS nodes, service times must be class-independent.
+##
+## @item V
+## @code{@var{V}(c,k)} is the average number of visits of class @math{c}
+## customers to service center @math{k} (@code{@var{V}(c,k) @geq{} 0}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at service center
+## @math{k}. Only single-server (@code{@var{m}(k)==1}) or IS (Infinite
+## Server) nodes (@code{@var{m}(k)<1}) are supported. If omitted, each
+## service center is assumed to have a single server. Queueing discipline
+## for single-server nodes can be FCFS, PS or LCFS-PR. 
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## @code{@var{U}(c,k)} is the
+## utilization of class @math{c} requests on service center @math{k}.
+##
+## @item R
+## @code{@var{R}(c,k)} is the response
+## time of class @math{c} requests on service center @math{k}.
+##
+## @item Q
+## @code{@var{Q}(c,k)} is the average number of
+## class @math{c} requests on service center @math{k}.
+##
+## @item X
+## @code{@var{X}(c,k)} is the class @math{c}
+## throughput on service center @math{k}.
+##
+## @end table
+##
+## @seealso{qnclosedmultimva, qnopenmulti}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnmix( lambda, N, S, V, m )
+  if ( nargin < 4 || nargin > 5 )
+    print_usage();
+  endif
+  isvector(lambda) || \
+      usage( "lambda must be a vector" );
+  lambda = lambda(:)';
+  isvector(N) || \
+      usage( "N must be a vector" );
+  N = N(:)';
+  size_equal(lambda,N) || \
+      usage( "lambda and N must be of equal length" );
+  ( !any( lambda>0 & N>0 ) ) || \
+      usage("A class cannot be open and closed at the same time. Check lambda and N" );
+  ( all( lambda>0 | N>0 ) ) || \
+      usage( "A class cannot be neither open nor closed. Check lambda and N" );
+  size_equal(S,V) || \
+      usage( "S and V must have the same size" );
+  C = length(lambda); # number of classes
+  K = columns(S); # number of service centers
+  rows(S) == C || \
+      usage( "S must have %d rows", C );
+  if ( nargin < 5 ) 
+    m = ones(1,K);
+  else
+    isvector( m ) || \
+        usage( "m must be a vector" );
+    m = m(:)';
+    size_equal(lambda,m) || \
+        usage( "lambda and m must be of equal length" );
+  endif
+  all( m<=1 ) || \
+      usage( "This function supports single-server and delay centers only. Check m" );
+  if ( !any(lambda>0) ) 
+    warning( "qnmix(): There are no open classes. Using qnclosedmultimva()" );
+    [U R Q X] = qnclosedmultimva( N, S, V, m );
+    return;
+  endif
+  if ( !any(N>0) ) 
+    warning( "qnmix(): There are no closed classes. Using qnopenmulti()" );
+    [U R Q X] = qnopenmulti( lambda, S, V, m );
+    return;
+  endif
+
+  D = S.*V; # service demands
+  op = find( lambda>0 ); # indexes of open networks
+  cl = find( N>0 ); # indexes of closed networks
+  
+  ## Initialize results
+  U = R = Q = X = zeros(C,K);
+  U(op,:) = diag( lambda(op) )* D(op,:); # U(c,:) = lambda(c)*D(c,:);
+  Uo = sum(U,1); # Total utilization for open classes on service center k
+  ## Build closed model to solve
+  Ncl = N(cl);
+  Scl = S;
+  for c=cl
+    Scl(c,:) = Scl(c,:) ./ (1-Uo);
+  endfor
+  Scl = Scl(cl,:); # select only rows for closed classes
+  Vcl = V(cl,:);
+  [Ucl Rcl Qcl Xcl] = qnclosedmultimva(Ncl, Scl, Vcl, m );
+  ## Results for closed classes
+  X(cl,:) = Xcl;
+  Q(cl,:) = Qcl;
+  R(cl,:) = Rcl;
+  U(cl,:) = X(cl,:) .* D(cl,:);
+  ## Results for open classes
+  Qc = sum(Q(cl,:),1);
+  i_single=find(m==1);
+  i_multi=find(m<1);
+  for c=op
+    R(c,i_single) = S(c,i_single).*(1+Qc) ./ (1-Uo); # This is the Response time, _not_ the residence time
+    R(c,i_multi) = S(c,i_multi);
+  endfor
+  Q(op,:) = (diag(lambda(op))*V(op,:)).*R(op,:); # Q(c,k) = lambda(c)*V(c,k)*R(c,k)
+  X(op,:) = U(op,:) ./ D(op,:); # X(c,k) = U(c,k)/D(c,k)
+endfunction
+%!test
+%! lambda = [1 0 0];
+%! N = [1 1 1];
+%! S = V = [1 1 1; 1 1 1; 1 1 1];
+%! fail( "qnmix( lambda, N, S, V)", "same time");
+%! N = [0 0 1];
+%! fail( "qnmix( lambda, N, S, V)", "open nor closed" );
+%! N = [0 1 2];
+%! m = [ 1 1 2 ];
+%! fail( "qnmix( lambda, N, S, V, m)", "single-server and delay" );
+%! S = V = [1 1 1; 1 1 1];
+%! fail( "qnmix( lambda, N, S, V)", "rows" );
+
+%!test
+%! # Example p. 148 Zahorjan et al.
+%! lambda = [1 1/2 0 0];
+%! N = [0 0 1 1];
+%! V = [1 1; 1 1; 1 1; 1 1];
+%! S = [1/4 1/6; 1/2 1; 1/2 1; 1 4/3];
+%! [U R Q X] = qnmix(lambda, N, S, V );
+%! assert( Q(3,1), 4/19, 1e-4 );
+%! assert( Q(3,2), 15/19, 1e-4 );
+%! assert( Q(4,1), 5/19, 1e-4 );
+%! assert( Q(4,2), 14/19, 1e-4 );
+
+%!test
+%! # Example 8.6 p. 345 Bolch et al.
+%! lambda = [0.5 0.25 0 0];
+%! N = [0 0 1 1];
+%! V = [2 1; 2.5 1.5; 1 0.5; 1 0.4];
+%! S = [0.4 0.6; 0.8 1.6; 0.3 0.5; 0.5 0.8];
+%! [U R Q X] = qnmix( lambda, N, S, V );
+%! assert( U([1 2],:), [0.4 0.3; 0.5 0.6], 1e-3 );
+%! assert( R([3 4],:), [4.829 6.951; 7.727 11.636], 1e-3 );
+%! assert( Q([3 4],:), [0.582 0.418; 0.624 0.376], 1e-3 );
+%! assert( Q([1 2],:), [8.822 5.383; 11.028 10.766], 1e-3 );
+%! assert( R([1 2],:), [8.822 10.766; 17.645 28.710], 1e-3 );
+%! assert( X(3,1)/V(3,1), 0.120, 1e-3 );
+%! assert( X(4,1)/V(4,1), 0.081, 1e-3 );
+
+%!test
+%! ## example figure 10 p. 26 Schwetman, "Implementing the Mean Value
+%! ## Analysis for the Solution of Queueing Network Models", Technical
+%! ## Report CSD-TR-355, feb 15, 1982, Purdue University. 
+%! S = [.25 0; .25 .10];
+%! V = [1 0; 1 1];
+%! lambda = [1 0];
+%! N = [0 3];
+%! [U R Q X] = qnmix( lambda, N, S, V );
+%! assert( U(1,1), .25, 1e-3 );
+%! assert( X(1,1), 1.0, 1e-3 );
+%! assert( [R(1,1) R(2,1) R(2,2)], [1.201 0.885 0.135], 1e-3 );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmknode.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,158 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{Q} =} qnmknode (@var{"m/m/m-fcfs"}, @var{S})
+## @deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"m/m/m-fcfs"}, @var{S}, @var{m})
+## @deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"m/m/1-lcfs-pr"}, @var{S})
+## @deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"-/g/1-ps"}, @var{S})
+## @deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"-/g/1-ps"}, @var{S}, @var{s2})
+## @deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"-/g/inf"}, @var{S})
+## @deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"-/g/inf"}, @var{S}, @var{s2})
+##
+## Creates a node; this function can be used together with
+## @code{qnsolve}. It is possible to create either single-class nodes
+## (where there is only one customer class), or multiple-class nodes
+## (where the service time is given per-class). Furthermore, it is
+## possible to specify load-dependent service times.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item S
+## Average service time. @math{S} can be either a scalar, a row vector,
+## a column vector or a two-dimensional matrix.
+##
+## @itemize
+##
+## @item If @math{S} is a scalar,
+## it is assumed to be a load-independent, class-independent service time.
+##
+## @item If @math{S} is a column vector, then @code{@var{S}(c)} is assumed to
+## the the load-independent service time for class @math{c} customers.
+##
+## @item If @math{S} is a row vector, then @code{@var{S}(n)} is assumed to be
+## the class-independent service time at the node, when there are @math{n}
+## requests. 
+##
+## @item Finally, if @var{S} is a two-dimensional matrix, then
+## @code{@var{S}(c,n)} is assumed to be the class @math{c} service time
+## when there are @math{n} requests at the node.
+##
+## @end itemize
+##
+## @item m
+## Number of identical servers at the node. Default is @code{@var{m}=1}.
+##
+## @item s2
+## Squared coefficient of variation for the service time. Default is 1.0.
+##
+## @end table
+##
+## The returned struct @var{Q} should be considered opaque to the client.
+##
+## @c The returned struct @var{Q} has the following fields:
+##
+## @c @table @var
+##
+## @c @item Q.node
+## @c (String) type of the node; valid values are @code{"m/m/m-fcfs"}, 
+## @c @code{"-/g/1-lcfs-pr"}, @code{"-/g/1-ps"} (Processor-Sharing) 
+## @c and @code{"-/g/inf"} (Infinite Server, or delay center).
+##
+## @c @item Q.S
+## @c Average service time. If @code{@var{Q}.S} is a vector, then
+## @c @code{@var{Q}.S(i)} is the average service time at that node
+## @c if there are @math{i} requests.
+##
+## @c @item Q.m
+## @c Number of identical servers at a @code{"m/m/m-fcfs"}. Default is 1.
+##
+## @c @item Q.c
+## @c Number of customer classes. Default is 1.
+##
+## @c @end table
+##
+## @seealso{qnsolve}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function Q = qnmknode( node, S, varargin )
+
+  ischar(node) || \
+      usage( "Parameter \"node\" must be a string" );
+
+  node = tolower(node);
+
+  isvector(S) || ismatrix(S) || \
+      usage( "Parameter \"S\" must be a vector" );
+  m = 1;
+  s2 = ones( size(S) );
+  if ( strcmp(node, "m/m/m-fcfs") )
+    ## M/M/k multiserver node
+    if ( nargin > 3 )
+      print_usage();
+    endif
+    if ( 3 == nargin )
+      m = varargin{1};
+      m>=1 || \
+	  usage( "m must be >=1" );
+    endif
+  elseif ( strcmp(node, "m/m/1/k-fcfs") )
+    ## M/M/1/k finite capacity node
+    if ( nargin > 3 )
+      print_usage();
+    endif
+    if ( 3 == nargin )
+      k = varargin{1};
+      k>=1 || \
+	  usage( "k must be >=1" );
+    endif
+  elseif ( strcmp(node, "-/g/1-lcfs-pr") )
+    ## -/G/1-LCFS-PR node
+    ( 2 == nargin || 3 == nargin ) || \
+	print_usage();
+    if ( 3 == nargin ) 
+      s2 = varargin{1};
+    endif
+  elseif ( strcmp(node, "-/g/1-ps") )
+    ## -/G/1-PS (processor sharing) node
+    ( 2 == nargin || 3 == nargin ) || \
+	print_usage();
+    if ( 3 == nargin )
+      s2 = varargin{1};
+    endif
+  elseif ( strcmp(node, "-/g/inf") )
+    ## -/G/inf (Infinite Server) node
+    ( 2 == nargin || 3 == nargin ) || \
+	print_usage();
+    if ( 3 == nargin )
+      s2 = varargin{1};
+    endif
+  else
+    usage( "Unknown node type \"%s\". node type must be one of \"m/m/m-fcfs\", \"-/g/1-lcfs-pr\", \"-/g/1-ps\" and \"-/g/inf\"", node );
+  endif
+  Q = struct( "node", node, "m", m, "S", S, "s2", s2, "c", rows(S), "comment", "" );
+endfunction
+%!test
+%! fail( "qnmknode( 'pippo', 1 )", "must be one" );
+%! fail( "qnmknode( '-/g/1-ps', 1, 1, 1)", "Invalid call" );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmm1.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,108 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}] =} qnmm1 (@var{lambda}, @var{mu})
+##
+## @cindex @math{M/M/1} system
+##
+## Compute utilization, response time, average number of requests
+## and throughput for a @math{M/M/1} queue.
+##
+## @iftex
+## The steady-state probability @math{\pi_k} that there are @math{k}
+## jobs in the system, @math{k \geq 0}, can be computed as:
+##
+## @tex
+## $$
+## \pi_k = (1-\rho)\rho^k
+## $$
+## @end tex
+##
+## where @math{\rho = \lambda/\mu} is the server utilization.
+##
+## @end iftex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate (@code{@var{lambda} > 0}).
+##
+## @item mu
+## Service rate (@code{@var{mu} > @var{lambda}}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## Server utilization
+##
+## @item R
+## Service center response time
+##
+## @item Q
+## Average number of requests in the system
+##
+## @item X
+## Service center throughput. If the system is ergodic, 
+## we will always have @code{@var{X} = @var{lambda}}
+##
+## @item p0
+## Steady-state probability that there are no requests in the system.
+##
+## @end table
+##
+## @var{lambda} and @var{mu} can be vectors of the same size. In this
+## case, the results will be vectors as well.
+##
+## @seealso{qnmmm, qnmminf, qnmmmk}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0] = qnmm1( lambda, mu )
+  if ( nargin != 2 )
+    print_usage();
+  endif
+  ## bring the parameters to a common size
+  [ err lambda mu ] = common_size( lambda, mu );
+  if ( err ) 
+    usage( "parameters are of incompatible size" );
+  endif
+  ( isvector(lambda) && isvector(mu) ) || \
+      usage( "lambda and mu must be vectors" );
+  all( lambda > 0 ) || \
+      usage( "lambda must be >0" );
+  all( mu > lambda ) || \
+      usage( "The system is not ergodic" );
+  U = rho = lambda ./ mu; # utilization
+  p0 = 1-rho;
+  Q = rho ./ (1-rho);
+  R = 1 ./ ( mu .* (1-rho) );
+  X = lambda;
+endfunction
+%!test
+%! fail( "qnmm1(10,5)", "not ergodic" );
+%! fail( "qnmm1([2 2], [1 1 1])", "incompatible size");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmm1k.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,149 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}, @var{pK}] =} qnmm1k (@var{lambda}, @var{mu}, @var{K})
+##
+## @cindex @math{M/M/1/K} system
+##
+## Compute utilization, response time, average number of requests and
+## throughput for a @math{M/M/1/K} finite capacity system. In a
+## @math{M/M/1/K} queue there is a single server; the maximum number of
+## requests in the system is @math{K}, and the maximum queue length is
+## @math{K-1}.
+##
+## @iftex
+## The steady-state probability @math{\pi_k} that there are @math{k}
+## jobs in the system, @math{0 @leq{} k @leq{} K}, can be computed as:
+##
+## @tex
+## $$
+## \pi_k = {(1-a)a^k \over 1-a^{K+1}}
+## $$
+## @end tex
+## where @math{a = \lambda/\mu}.
+## @end iftex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate (@code{@var{lambda}>0}).
+##
+## @item mu
+## Service rate (@code{@var{mu}>0}).
+##
+## @item K
+## Maximum number of requests allowed in the system (@code{@var{K} @geq{} 1}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## Service center utilization, which is defined as @code{@var{U} = 1-@var{p0}}
+##
+## @item R
+## Service center response time
+##
+## @item Q
+## Average number of requests in the system
+##
+## @item X
+## Service center throughput
+##
+## @item p0
+## Steady-state probability that there are no requests in the system
+##
+## @item pK
+## Steady-state probability that there are @math{K} requests in the system
+## (i.e., that the system is full)
+##
+## @end table
+##
+## @var{lambda}, @var{mu} and @var{K} can be vectors of the
+## same size. In this case, the results will be vectors as well.
+##
+## @seealso{qnmm1,qnmminf,qnmmm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pK] = qnmm1k( lambda, mu, K )
+  if ( nargin != 3 )
+    print_usage();
+  endif
+
+  ( isvector(lambda) && isvector(mu) && isvector(K) ) || \
+      usage( "lambda, mu, K must be vectors of the same size" );
+
+  [err lambda mu K] = common_size( lambda, mu, K );
+  if ( err ) 
+    usage( "Parameters are not of common size" );
+  endif
+
+  all( K>0 ) || \
+      usage( "K must be >0" );
+  ( all( lambda>0 ) && all( mu>0 ) ) || \
+      usage( "lambda and mu must be >0" );
+
+  U = R = Q = X = p0 = pK = 0*lambda;
+  a = lambda./mu;
+  ## persistent tol = 1e-7;
+  ## if a!=1
+  ## i = find( abs(a-1)>tol );
+  i = find( a != 1 );
+  p0(i) = (1-a(i))./(1-a(i).^(K(i)+1));
+  pK(i) = (1-a(i)).*(a(i).^K(i))./(1-a(i).^(K(i)+1));
+  Q(i) = a(i)./(1-a(i)) - (K(i)+1)./(1-a(i).^(K(i)+1)).*(a(i).^(K(i)+1));
+  ## if a==1
+  ## i = find( abs(a-1)<=tol );
+  i = find( a == 1 );
+  p0(i) = pK(i) = 1./(K(i)+1);
+  Q(i) = K(i)/2;   
+  ## Compute other performance measures
+  U = 1-p0;
+  X = lambda.*(1-pK);
+  R = Q ./ X;
+endfunction
+%!test
+%! lambda = mu = 1;
+%! K = 10;
+%! [U R Q X p0] = qnmm1k(lambda,mu,K);
+%! assert( Q, K/2, 1e-7 );
+%! assert( U, 1-p0, 1e-7 );
+
+%!test
+%! # Compare result with one obtained by solvind the CTMC
+%! lambda = 0.8;
+%! mu = 0.8;
+%! K = 10;
+%! [U1 R1 Q1 X1] = qnmm1k( lambda, mu, K );
+%! birth = lambda*ones(1,K);
+%! death = mu*ones(1,K);
+%! q = ctmc_bd( birth, death );
+%! U2 = 1-q(1);
+%! Q2 = dot( [0:K], q );
+%! assert( U1, U2, 1e-4 );
+%! assert( Q1, Q2, 1e-4 );
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmminf.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,110 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}] =} qnmminf (@var{lambda}, @var{mu})
+##
+## Compute utilization, response time, average number of requests and
+## throughput for a @math{M/M/\infty} queue. This is a system with an
+## infinite number of identical servers. Note that a @math{M/M/\infty}
+## system is always stable, regardless the values of the arrival and
+## service rates.
+##
+## @cindex @math{M/M/}inf system
+##
+## @iftex
+## The steady-state probability @math{\pi_k} that there are @math{k}
+## requests in the system, @math{k @geq{} 0}, can be computed as:
+##
+## @tex
+## $$
+## \pi_k = {1 \over k!} \left( \lambda \over \mu \right)^k e^{-\lambda / \mu}
+## $$
+## @end tex 
+## @end iftex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate (@code{@var{lambda}>0}).
+##
+## @item mu
+## Service rate (@code{@var{mu}>0}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## Traffic intensity (defined as @math{\lambda/\mu}). Note that this is
+## different from the utilization, which in the case of @math{M/M/\infty}
+## centers is always zero.
+##
+## @cindex traffic intensity
+##
+## @item R
+## Service center response time.
+##
+## @item Q
+## Average number of requests in the system (which is equal to the
+## traffic intensity @math{\lambda/\mu}).
+##
+## @item X
+## Throughput (which is always equal to @code{@var{X} = @var{lambda}}).
+##
+## @item p0
+## Steady-state probability that there are no requests in the system
+##
+## @end table
+##
+## @var{lambda} and @var{mu} can be vectors of the same size. In this
+## case, the results will be vectors as well.
+##
+## @seealso{qnmm1,qnmmm,qnmmmk}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0] = qnmminf( lambda, mu )
+  if ( nargin != 2 )
+    print_usage();
+  endif
+  [ err lambda mu ] = common_size( lambda, mu );
+
+  if ( err ) 
+    usage( "Parameters are of incompatible size" );
+  endif
+  
+  ( isvector(lambda) && isvector(mu) ) || \
+      usage( "lambda and mu must be vectors" );
+  ( all( lambda>0 ) && all( mu>0 ) ) || \
+      usage( "lambda and mu must be >0" );
+  U = Q = lambda ./ mu; # Traffic intensity.
+  p0 = exp(-lambda./mu); # probability that there are 0 requests in the system
+  R = 1 ./ mu;
+  X = lambda;
+endfunction
+%!test
+%! fail( "qnmminf( [1 2], [1 2 3] )", "incompatible size");
+%! fail( "qnmminf( [-1 -1], [1 1] )", ">0" );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmmm.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,158 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}, @var{pm}] =} qnmmm (@var{lambda}, @var{mu})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}, @var{pm}] =} qnmmm (@var{lambda}, @var{mu}, @var{m})
+##
+## @cindex @math{M/M/m} system
+##
+## Compute utilization, response time, average number of requests in
+## service and throughput for a @math{M/M/m} queue, a queueing
+## system with @math{m} identical service centers connected to a single queue.
+##
+## @iftex
+## The steady-state probability @math{\pi_k} that there are @math{k}
+## jobs in the system, @math{k \geq 0}, can be computed as:
+##
+## @tex
+## $$
+## \pi_k = \cases{ \displaystyle{\pi_0 { ( m\rho )^k \over k!}} & $0 \leq k \leq m$;\cr
+##                 \displaystyle{\pi_0 { \rho^k m^m \over m!}} & $k>m$.\cr
+## }
+## $$
+## @end tex
+##
+## where @math{\rho = \lambda/(m\mu)} is the individual server utilization.
+## The steady-state probability @math{\pi_0} that there are no jobs in the
+## system can be computed as:
+##
+## @tex
+## $$
+## \pi_0 = \left[ \sum_{k=0}^{m-1} { (m\rho)^k \over k! } + { (m\rho)^m \over m!} {1 \over 1-\rho} \right]^{-1}
+## $$
+## @end tex
+##
+## @end iftex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate (@code{@var{lambda}>0}).
+##
+## @item mu
+## Service rate (@code{@var{mu}>@var{lambda}}).
+##
+## @item m
+## Number of servers (@code{@var{m} @geq{} 1}).
+## If omitted, it is assumed @code{@var{m}=1}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## Service center utilization, @math{U = \lambda / (m \mu)}.
+##
+## @item R
+## Service center response time
+##
+## @item Q
+## Average number of requests in the system
+##
+## @item X
+## Service center throughput. If the system is ergodic, 
+## we will always have @code{@var{X} = @var{lambda}}
+##
+## @item p0
+## Steady-state probability that there are 0 requests in the system
+##
+## @item pm
+## Steady-state probability that an arriving request has to wait in the
+## queue
+##
+## @end table
+##
+## @var{lambda}, @var{mu} and @var{m} can be vectors of the same size. In this
+## case, the results will be vectors as well.
+##
+## @seealso{qnmm1,qnmminf,qnmmmk}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pm] = qnmmm( lambda, mu, m )
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+  if ( nargin == 2 )
+    m = 1;
+  endif
+  [err lambda mu m] = common_size( lambda, mu, m );
+  if ( err ) 
+    usage( "parameters are not of common size" );
+  endif
+
+  ( isvector(lambda) && isvector(mu) && isvector(m) ) || \
+      usage( "the parameters must be vectors" );
+  
+  all( m>0 ) || \
+      usage( "m must be >0" );
+
+  all( lambda < m .* mu ) || \
+      error( "Processing capacity exceeded" );
+
+  X = lambda;
+  U = rho = lambda ./ (m .* mu );
+  Q = p0 = 0*lambda;
+  for i=1:length(lambda) # I cannot easily vectorize this
+    #p = zeros(1,m(i)+1);
+    k=[0:m(i)-1];
+    p0(i) = 1 / ( ...
+                 sum( (m(i)*rho(i)).^ k ./ factorial(k)) + ...
+                 (m(i)*rho(i))^m(i) / (factorial(m(i))*(1-rho(i))) ...
+                 );
+    #p(2+k) = p(1)*( m(i)*rho(i) ).^(1+k)./factorial(1+k);
+    #U(i) = 1-dot( (m(i)-k)./m(i), p(k+1) ); # FIXME: check
+  endfor
+  pm = (m.*rho).^m./(factorial(m).*(1-rho)).*p0;
+  Q = m .* rho .+ rho ./ (1-rho) .* pm;
+  R = Q ./ X;
+endfunction
+%!demo
+%! disp("This is figure 6.4 on p. 220 Bolch et al.");
+%! rho = 0.9;
+%! ntics = 21;
+%! lambda = 0.9;
+%! m = linspace(1,ntics,ntics);
+%! mu = lambda./(rho .* m);
+%! [U R Q X] = qnmmm(lambda, mu, m);
+%! qlen = X.*(R-1./mu);
+%! plot(m,Q,"o",qlen,"*");
+%! axis([0,ntics,0,25]);
+%! legend("Jobs in the system","Queue Length","location","northwest");
+%! xlabel("Number of servers (m)");
+%! title("\lambda = 0.9, \mu = 0.9");
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmmmk.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,223 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}, @var{pK}] =} qnmmmk (@var{lambda}, @var{mu}, @var{m}, @var{K})
+##
+## @cindex @math{M/M/m/K} system
+##
+## Compute utilization, response time, average number of requests and
+## throughput for a @math{M/M/m/K} finite capacity system. In a
+## @math{M/M/m/K} system there are @math{m \geq 1} identical service
+## centers sharing a fixed-capacity queue. At any time, at most @math{K @geq{} m} requests can be in the system. The maximum queue length
+## is @math{K-m}. This function generates and
+## solves the underlying CTMC.
+##
+## @iftex
+##
+## The steady-state probability @math{\pi_k} that there are @math{k}
+## jobs in the system, @math{0 @leq{} k @leq{} K} can be expressed as:
+##
+## @tex
+## $$
+## \pi_k = \cases{ \displaystyle{{\rho^k \over k!} \pi_0} & if $0 \leq k \leq m$;\cr
+##                 \displaystyle{{\rho^m \over m!} \left( \rho \over m \right)^{k-m} \pi_0} & if $m < k \leq K$\cr}
+## $$
+## @end tex
+##
+## where @math{\rho = \lambda/\mu} is the offered load. The probability
+## @math{\pi_0} that the system is empty can be computed by considering
+## that all probabilities must sum to one: @math{\sum_{k=0}^K \pi_k = 1},
+## which gives:
+##
+## @tex
+## $$
+## \pi_0 = \left[ \sum_{k=0}^m {\rho^k \over k!} + {\rho^m \over m!} \sum_{k=m+1}^K \left( {\rho \over m}\right)^{k-m} \right]^{-1}
+## $$
+## @end tex
+##
+## @end iftex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate (@code{@var{lambda}>0}).
+##
+## @item mu
+## Service rate (@code{@var{mu}>0}).
+##
+## @item m
+## Number of servers (@code{@var{m} @geq{} 1}).
+##
+## @item K
+## Maximum number of requests allowed in the system,
+## including those inside the service centers
+## (@code{@var{K} @geq{} @var{m}}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## Service center utilization
+##
+## @item R
+## Service center response time
+##
+## @item Q
+## Average number of requests in the system
+##
+## @item X
+## Service center throughput
+##
+## @item p0
+## Steady-state probability that there are no requests in the system.
+##
+## @item pK
+## Steady-state probability that there are @var{K} requests in the system
+## (i.e., probability that the system is full).
+##
+## @end table
+##
+## @var{lambda}, @var{mu}, @var{m} and @var{K} can be either scalars, or
+## vectors of the  same size. In this case, the results will be vectors
+## as well.
+##
+## @seealso{qnmm1,qnmminf,qnmmm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pK] = qnmmmk( lambda, mu, m, K )
+  if ( nargin != 4 )
+    print_usage();
+  endif
+
+  ( isvector(lambda) && isvector(mu) && isvector(m) && isvector(K) ) || ...
+      usage( "lambda, mu, m, K must be vectors of the same size" );
+  lambda = lambda(:)'; # make lambda a row vector
+  mu = mu(:)'; # make mu a row vector
+  m = m(:)'; # make m a row vector
+  K = K(:)'; # make K a row vector
+
+  [err lambda mu m K] = common_size( lambda, mu, m, K );
+  if ( err ) 
+    usage( "Parameters are not of common size" );
+  endif
+
+  all( K>0 ) || \
+      usage( "k must be strictly positive" );
+  all( m>0 ) && all( m <= K ) || \
+      usage( "m must be in the range 1:k" );
+  all( lambda>0 ) && all( mu>0 ) || \
+      usage( "lambda and mu must be >0" );
+  U = R = Q = X = p0 = pK = 0*lambda;
+  for i=1:length(lambda)
+    ## Build and solve the birth-death process describing the M/M/m/k system
+    birth_rate = lambda(i)*ones(1,K(i));
+    death_rate = [ linspace(1,m(i),m(i))*mu(i) ones(1,K(i)-m(i))*m(i)*mu(i) ];
+    p = ctmc_bd(birth_rate, death_rate);
+    p0(i) = p(1);
+    pK(i) = p(1+K(i));
+    j = [1:K(i)];
+    Q(i) = dot( p(1+j),j );
+  endfor
+  ## Compute other performance measures
+  X = lambda.*(1-pK);
+  U = X ./ (m .* mu );
+  R = Q ./ X;
+endfunction
+%!test
+%! lambda = mu = m = 1;
+%! k = 10;
+%! [U R Q X p0] = qnmmmk(lambda,mu,m,k);
+%! assert( Q, k/2, 1e-7 );
+%! assert( U, 1-p0, 1e-7 );
+
+%!test
+%! lambda = [1 0.8 2 9.2 0.01];
+%! mu = lambda + 0.17;
+%! k = 12;
+%! [U1 R1 Q1 X1] = qnmm1k(lambda,mu,k);
+%! [U2 R2 Q2 X2] = qnmmmk(lambda,mu,1,k);
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 1e-5 );
+%! assert( X1, X2, 1e-5 );
+%! #assert( [U1 R1 Q1 X1], [U2 R2 Q2 X2], 1e-5 );
+
+%!test
+%! lambda = 0.9;
+%! mu = 0.75;
+%! k = 10;
+%! [U1 R1 Q1 X1 p01] = qnmmmk(lambda,mu,1,k);
+%! [U2 R2 Q2 X2 p02] = qnmm1k(lambda,mu,k);
+%! assert( [U1 R1 Q1 X1 p01], [U2 R2 Q2 X2 p02], 1e-5 );
+
+%!test
+%! lambda = 0.8;
+%! mu = 0.85;
+%! m = 3;
+%! k = 5;
+%! [U1 R1 Q1 X1 p0] = qnmmmk( lambda, mu, m, k );
+%! birth = lambda*ones(1,k);
+%! death = [ mu*linspace(1,m,m) mu*m*ones(1,k-m) ];
+%! q = ctmc_bd( birth, death );
+%! U2 = dot( q, min( 0:k, m )/m );
+%! assert( U1, U2, 1e-4 );
+%! Q2 = dot( [0:k], q );
+%! assert( Q1, Q2, 1e-4 );
+%! assert( p0, q(1), 1e-4 );
+
+%!test
+%! # This test comes from an example I found on the web 
+%! lambda = 40;
+%! mu = 30;
+%! m = 3;
+%! k = 7;
+%! [U R Q X p0] = qnmmmk( lambda, mu, m, k );
+%! assert( p0, 0.255037, 1e-6 );
+%! assert( R, 0.036517, 1e-6 );
+
+%!test
+%! # This test comes from an example I found on the web 
+%! lambda = 50;
+%! mu = 10;
+%! m = 4;
+%! k = 6;
+%! [U R Q X p0 pk] = qnmmmk( lambda, mu, m, k );
+%! assert( pk, 0.293543, 1e-6 );
+
+%!test
+%! # This test comes from an example I found on the web 
+%! lambda = 3;
+%! mu = 2;
+%! m = 2;
+%! k = 5;
+%! [U R Q X p0 pk] = qnmmmk( lambda, mu, m, k );
+%! assert( p0, 0.179334, 1e-6 );
+%! assert( pk, 0.085113, 1e-6 );
+%! assert( Q, 2.00595, 1e-5 );
+%! assert( R-1/mu, 0.230857, 1e-6 ); # waiting time in the queue
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmvablo.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,198 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmvablo (@var{N}, @var{S}, @var{M}, @var{P})
+##
+## @cindex queueing network with blocking
+## @cindex blocking queueing network
+## @cindex closed network, finite capacity
+##
+## MVA algorithm for closed queueing networks with blocking. @command{qnmvablo}
+## computes approximate utilization, response time and mean queue length
+## for closed, single class queueing networks with blocking.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## population size, i.e., number of requests in the system. @var{N} must
+## be strictly greater than zero, and less than the overall network capacity:
+## @code{0 < @var{N} < sum(@var{M})}.
+##
+## @item S
+## Average service time. @code{@var{S}(i)} is the average service time 
+## requested on server @math{i} (@code{@var{S}(i) > 0}).
+##
+## @item M
+## Server capacity. @code{@var{M}(i)} is the capacity of service center
+## @math{i}. The capacity is the maximum number of requests in a service
+## center, including the request currently in service (@code{@var{M}(i) @geq{} 1}).
+##
+## @item P
+## @code{@var{P}(i,j)} is the probability that a request which completes
+## service at server @math{i} will be transferred to server @math{j}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## @code{@var{U}(i)} is the utilization of
+## service center @math{i}.
+##
+## @item R
+## @code{@var{R}(i)} is the average response time
+## of service center @math{i}.
+##
+## @item Q
+## @code{@var{Q}(i)} is
+## the average number of requests in service center @math{i} (including
+## the request in service).
+##
+## @item X
+## @code{@var{X}(i)} is the throughput of
+## service center @math{i}.
+##
+## @end table
+##
+## @seealso{qnopen, qnclosed}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnmvablo( K, S, M, P )
+
+  ## Note that we use "K" instead of "N" as the number of requests in
+  ## order to be compliant with the paper by Akyildiz describing this
+  ## algorithm.
+
+  if ( nargin != 4 ) 
+    print_usage();
+  endif
+  ( isscalar(K) && K > 0 ) || \
+      usage( "K must be a positive integer" );
+  isvector(S) && all(S>0) || \
+      error ("S must be a vector > 0");
+  S = S(:)'; # make S a row vector
+  N = length(S);
+  ( isvector(M) && length(M) == N ) || \
+      usage( "M must be a vector with %d elements", N );
+  all( M >= 1) || \
+      usage( "M must be >= 1");
+  M = M(:)'; # make M a row vector
+
+  (K < sum(M)) || \
+      error( "The population size K=%d exceeds the total system capacity %d", K, sum(M) );
+  dtmc_check_P(P);
+  rows(P) == N || \
+      error("The number of rows of P must be equal to the length of S");
+
+  ## Note: in this implementation we make use of the same notation found
+  ## in Akyildiz's paper cited in the REFERENCES above, with the minor
+  ## exception of using 'v' instead of 'e' as the visit count vector.
+  ## k_bar(i) is the average number of jobs in the i-th server, lambda
+  ## is the network throughput, t_bar(i) is the mean residence time
+  ## (time spent in queue and in service) for requests in the i-th
+  ## service center.
+
+  ## Initialization
+  k_bar_m1 = zeros(1,N); # k_bar(k-1)
+  BT = zeros(1,N);
+  z = ones(1,N);
+  lambda = 0;
+  ## Computation of the visit counts
+  v = qnvisits(P);
+  D = S .* v; # Service demand
+  ## Main loop
+  for k=1:K
+    do
+    ## t_bar_i(k) = S(i) *(z_i(k) + k_bar_i(k-1))+BT_i(k)
+      t_bar = S .* ( z + k_bar_m1 ) + BT; 
+      lambda = k / dot(v,t_bar);
+      k_bar = t_bar .* v * lambda;
+      if ( any(k_bar>M) )
+        i = find( k_bar > M, 1 );
+        z(i) = 0;
+        BT = BT + S(i) * ( v .* P(:,i)' ) / v(i);
+      endif
+    until( all(k_bar<=M) );
+    k_bar_m1 = k_bar;
+  endfor
+  R = t_bar;
+  X = v * lambda; # Throughputs
+  ## w_bar = t_bar - S - BT; # mean waiting time
+  U = X .* S;
+  Q = X .* R;
+endfunction
+%!test
+%! fail( "qnmvablo( 10, [1 1], [4 5], [0 1; 1 0] )", "capacity");
+%! fail( "qnmvablo( 6, [1 1], [4 5], [0 1; 1 1] )", "stochastic");
+%! fail( "qnmvablo( 5, [1 1 1], [1 1], [0 1; 1 1] )", "3 elements");
+
+%!test
+%! # This is the example on section v) p. 422 of the reference paper
+%! M = [12 10 14];
+%! P = [0 1 0; 0 0 1; 1 0 0];
+%! S = [1/1 1/2 1/3];
+%! K = 27;
+%! [U R Q X]=qnmvablo( K, S, M, P );
+%! assert( R, [11.80 1.66 14.4], 1e-2 );
+
+%!test
+%! # This is example 2, i) and ii) p. 424 of the reference paper
+%! M = [4 5 5];
+%! S = [1.5 2 1];
+%! P = [0 1 0; 0 0 1; 1 0 0];
+%! K = 10;
+%! [U R Q X]=qnmvablo( K, S, M, P );
+%! assert( R, [6.925 8.061 4.185], 1e-3 );
+%! K = 12;
+%! [U R Q X]=qnmvablo( K, S, M, P );
+%! assert( R, [7.967 9.019 8.011], 1e-3 );
+
+%!test
+%! # This is example 3, i) and ii) p. 424 of the reference paper
+%! M = [8 7 6];
+%! S = [0.2 1.2 1.4];
+%! P = [ 0 0.5 0.5; 1 0 0; 1 0 0 ];
+%! K = 10;
+%! [U R Q X] = qnmvablo( K, S, M, P );
+%! assert( R, [1.674 5.007 7.639], 1e-3 );
+%! K = 12;
+%! [U R Q X] = qnmvablo( K, S, M, P );
+%! assert( R, [2.166 5.372 6.567], 1e-3 );
+
+%!test
+%! # Network which never blocks, central server model
+%! M = [50 50 50];
+%! S = [1 1/0.8 1/0.4];
+%! P = [0 0.7 0.3; 1 0 0; 1 0 0];
+%! K = 40;
+%! [U1 R1 Q1] = qnmvablo( K, S, M, P );
+%! V = qnvisits(P);
+%! [U2 R2 Q2] = qnclosedsinglemva( K, S, V );
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 1e-5 );
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnmvapop.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,85 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {@var{H} =} qnmvapop (@var{N})
+##
+## @cindex population mix
+## @cindex closed network, multiple classes
+##
+## Given a network with @math{C} customer classes, this function
+## computes the number of valid population mixes @code{@var{H}(r,n)} that can
+## be constructed by the multiclass MVA algorithm by allocating @math{n}
+## customers to the first @math{r} classes.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## Population vector. @code{@var{N}(c)} is the number of class-@math{c}
+## requests in the system. The total number of requests in the network
+## is @code{sum(@var{N})}.
+## 
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item H
+## @code{@var{H}(r,n)} is the number of valid populations that can be
+## constructed allocating @math{n} customers to the first @math{r} classes.
+##
+## @end table
+##
+## @seealso{qnclosedmultimva,population_mix}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function H = qnmvapop( N )
+  (isvector(N) && all( N > 0 ) ) || \
+      error( "N must be a vector of strictly positive integers" );
+  N = N(:)'; # make N a row vector
+  Ns = sum(N);
+  R = length(N);
+  
+  ## Please note that the algorithm as described in the reference (see
+  ## documentation in PDF format) seems incorrect: in the implementation
+  ## above the @code{TOTAL_POP} variable is initialized with
+  ## @code{@var{N}(1)}, instead of 0 as in the paper. Moreover, here the
+  ## @code{TOTAL_POP} variable is incremented by @code{@var{N}(r)} at
+  ## each iteration (instead of @code{@var{N}(r-1)} as in the paper)
+  
+  total_pop = N(1);
+  H = zeros(R, Ns+1);
+  H(1,1:N(1)+1) = 1;
+  for r=2:R
+    total_pop += N(r);
+    for n=0:total_pop
+      range = max(0,n-N(r)) : n;
+      H(r,n+1) = sum( H(r-1, range+1 ) );
+    endfor
+  endfor
+endfunction
+%!test
+%! H = qnmvapop( [1 2 2] );
+%! assert( H, [1 1 0 0 0 0; 1 2 2 1 0 0; 1 3 5 5 3 1] );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnopen.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,66 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnopen (@var{lambda}, @var{S}, @var{V}, @dots{})
+##
+## @cindex open network
+##
+## Compute utilization, response time, average number of requests in the
+## system, and throughput for open queueing networks. If @var{lambda} is
+## a scalar, the network is considered a single-class QN and is solved
+## using @code{qnopensingle}. If @var{lambda} is a vector, the network
+## is considered as a multiclass QN and solved using @code{qnopenmulti}.
+##
+## @seealso{qnopensingle, qnopenmulti}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnopen( lambda, S, V, varargin )
+  if ( nargin < 3 )
+    print_usage();
+  endif
+  if ( isscalar(lambda) )
+    [U R Q X] = qnopensingle(lambda, S, V, varargin{:});
+  else
+    [U R Q X] = qnopenmulti(lambda, S, V, varargin{:});
+  endif
+endfunction
+%!test
+%! # Example 34.1 p. 572
+%! lambda = 3;
+%! V = [16 7 8];
+%! S = [0.01 0.02 0.03];
+%! [U R Q X] = qnopen( lambda, S, V );
+%! assert( R, [0.0192 0.0345 0.107], 1e-2 );
+%! assert( U, [0.48 0.42 0.72], 1e-2 );
+
+%!test
+%! V = [1 1; 1 1];
+%! S = [1 3; 2 4];
+%! lambda = [3/19 2/19];
+%! [U R Q] = qnopen(lambda, S, V);
+%! assert( U(1,1), 3/19, 1e-6 );
+%! assert( U(2,1), 4/19, 1e-6 );
+%! assert( R(1,1), 19/12, 1e-6 );
+%! assert( R(1,2), 57/2, 1e-6 );
+%! assert( Q(1,1), .25, 1e-6 );
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnopenab.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,79 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{Xu}, @var{Rl}] =} qnopenab (@var{lambda}, @var{D})
+##
+## @cindex bounds, asymptotic
+## @cindex open network
+##
+## Compute Asymptotic Bounds for single-class, open Queueing Networks
+## with @math{K} service centers.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## overall arrival rate to the system (scalar). Abort if
+## @code{@var{lambda} @leq{} 0}
+##
+## @item D
+## @code{@var{D}(k)} is the service demand at center @math{k}.
+## The service demand vector @var{D} must be nonempty, and all demands
+## must be nonnegative (@code{@var{D}(k) @geq{} 0} for all @math{k}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xu
+## Upper bound on the system throughput.
+##
+## @item Rl
+## Lower bound on the system response time.
+##
+## @end table
+##
+## @seealso{qnopenbsb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_upper R_lower] = qnopenab( lambda, D )
+  if ( nargin != 2 )
+    print_usage();
+  endif
+  ( isscalar(lambda) && lambda > 0 ) || \
+      usage( "lambda must be a positive scalar" );
+  ( isvector(D) && length(D)>0 && all( D>=0 ) ) || \
+      usage( "D must be a vector of nonnegative scalars" );
+
+  X_upper = 1/max(D);
+  R_lower = sum(D);
+endfunction
+
+%!test
+%! fail( "qnopenab( 0.1, [] )", "vector" );
+%! fail( "qnopenab( 0.1, [0 -1])", "vector" );
+%! fail( "qnopenab( 0, [1 2] )", "lambda" );
+%! fail( "qnopenab( -1, [1 2])", "lambda" );
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnopenbsb.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,85 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{Xu}, @var{Rl}, @var{Ru}] =} qnopenbsb (@var{lambda}, @var{D})
+##
+## @cindex bounds, balanced system
+## @cindex open network
+##
+## Compute Balanced System Bounds for single-class, open Queueing Networks
+## with @math{K} service centers.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda 
+## overall arrival rate to the system (scalar). Abort if
+## @code{@var{lambda} < 0 }
+##
+## @item D 
+## @code{@var{D}(k)} is the service demand at center @math{k}.
+## The service demand vector @var{D} must be nonempty, and all demands
+## must be nonnegative (@code{@var{D}(k) @geq{} 0} for all @math{k}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## Lower bound on the system throughput.
+##
+## @item Rl
+## @itemx Ru
+## Lower and upper bound on the system response time.
+##
+## @end table
+##
+## @seealso{qnopenab}
+## 
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_upper R_lower R_upper] = qnopenbsb( lambda, D )
+  if ( nargin != 2 )
+    print_usage();
+  endif
+  ( isscalar(lambda) && lambda>0 ) || \
+      usage( "lambda must be a positive scalar" );
+  ( isvector(D) && length(D)>0 && all(D>=0) ) || \
+      usage( "D must be a vector of nonnegative floats" );
+
+  D_max = max(D);
+  D_tot = sum(D);
+  D_ave = mean(D_tot);
+  X_upper = 1/D_max;
+  R_lower = D_tot / (1-lambda*D_ave);
+  R_upper = D_tot / (1-lambda*D_max);
+endfunction
+
+%!test
+%! fail( "qnopenbsb( 0.1, [] )", "vector" );
+%! fail( "qnopenbsb( 0.1, [0 -1])", "vector" );
+%! fail( "qnopenbsb( 0, [1 2] )", "lambda" );
+%! fail( "qnopenbsb( -1, [1 2])", "lambda" );
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnopenmulti.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,157 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnopenmulti (@var{lambda}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnopenmulti (@var{lambda}, @var{S}, @var{V}, @var{m})
+##
+## @cindex open network, multiple classes
+##
+## Exact analysis of open, multiple-class BCMP networks. The network can
+## be made of @emph{single-server} queueing centers (FCFS, LCFS-PR or
+## PS) or delay centers (IS). This function assumes a network with
+## @math{K} service centers and @math{C} customer classes.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## @code{@var{lambda}(c)} is the external
+## arrival rate of class @math{c} customers (@code{@var{lambda}(c)>0}).
+##
+## @item S
+## @code{@var{S}(c,k)} is the mean service time of class @math{c}
+## customers on the service center @math{k} (@code{@var{S}(c,k)>0}).
+## For FCFS nodes, average service times must be class-independent.
+##
+## @item V
+## @code{@var{V}(c,k)} is the average number of visits of class @math{c}
+## customers to service center @math{k} (@code{@var{V}(c,k) @geq{} 0 }).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at service center
+## @math{k}. Valid values are @code{@var{m}(k) < 1} to denote a delay
+## center (@math{-/G/\infty}), and @code{@var{m}(k)==1} to denote
+## a single server queueing center (@math{M/M/1}--FCFS,
+## @math{-/G/1}--LCFS-PR or @math{-/G/1}--PS).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## If @math{k} is a queueing center, then @code{@var{U}(c,k)} is the
+## class @math{c} utilization of center @math{k}. If @math{k} is
+## an IS node, then @code{@var{U}(c,k)} is the 
+## class @math{c} @emph{traffic intensity}
+## defined as @code{@var{X}(c,k)*@var{S}(c,k)}.
+##
+## @item R
+## @code{@var{R}(c,k)} is the class @math{c} response time at
+## center @math{k}. The system response time for
+## class @math{c} requests can be computed
+## as @code{dot(@var{R}, @var{V}, 2)}.
+##
+## @item Q
+## @code{@var{Q}(c,k)} is the average number of class @math{c} requests
+## at center @math{k}. The average number of class @math{c} requests
+## in the system @var{Qc} can be computed as @code{Qc = sum(@var{Q}, 2)}
+##
+## @item X
+## @code{@var{X}(c,k)} is the class @math{c} throughput
+## at center @math{k}.
+##
+## @end table
+##
+## @seealso{qnopen,qnopensingle,qnvisits}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+function [U R Q X] = qnopenmulti( lambda, S, V, m )
+  if ( nargin < 3 || nargin > 4 )
+    print_usage();
+  endif
+  isvector(lambda) && all(lambda > 0) || \
+      usage( "lambda must be a vector of positive floats" );
+  lambda = lambda(:)'; # make lambda a row vector
+  C = length(lambda);
+  K = columns(S);
+  [C,K] == size(S) || \
+      usage( "rows(S) must be equal to length(lambda)" );
+  all(all( S > 0 )) || \
+      usage( "S(c,k) must be > 0" );
+  [C,K] == size(V) || \
+      usage( "V must be a matrix of the same size as S" );
+  all( all(V>= 0) ) || \
+      usage( "V must be >= 0 " );
+
+  if ( nargin < 4 )
+    m = ones(1,K);
+  else
+    ( isvector( m ) && length(m) == K && all( m <= 1 ) ) || \
+        usage( "m must be less than or equal to ones(1,K)" );
+    m = m(:)'; # make m a row vector
+  endif
+
+  D = S .* V; # Service demands: D(c,k) = S(c,k) * V(c,k)
+  max(lambda * D) < 1 || \
+      error( "processing capacity exceeded" );
+
+  U = R = Q = X = zeros(C,K);
+  i_delay  = find(m<1);
+  i_single = find(m==1);
+  U = diag(lambda)*D; # U(c,:) = lambda(c)*D(c,:);
+  X = diag(lambda)*V; # X(c,:) = lambda(c)*V(c,:);
+  
+  ## delay centers
+  R(:,i_delay) = S(:,i_delay);
+  Q(:,i_delay) = U(:,i_delay);
+
+  ## Queueing centers
+  for c=1:C
+    R(c,i_single) = S(c,i_single) ./ ( 1 - sum(U(:,i_single),1) );
+    Q(c,i_single) = U(c,i_single) ./ ( 1 - sum(U(:,i_single),1) );
+  endfor
+endfunction
+%!test
+%! V = [1 1; 1 1];
+%! S = [1 3; 2 4];
+%! lambda = [3/19 2/19];
+%! [U R Q] = qnopenmulti(lambda, S, V);
+%! assert( U(1,1), 3/19, 1e-6 );
+%! assert( U(2,1), 4/19, 1e-6 );
+%! assert( R(1,1), 19/12, 1e-6 );
+%! assert( R(1,2), 57/2, 1e-6 );
+%! assert( Q(1,1), .25, 1e-6 );
+
+%!test
+%! # example p. 138 Zahorjan et al.
+%! V = [ 10 9; 5 4];
+%! S = [ 1/10 1/3; 2/5 1];
+%! lambda = [3/19 2/19];
+%! [U R Q X] = qnopenmulti(lambda, S, V);
+%! assert( X(1,1), 1.58, 1e-2 );
+%! assert( U(1,1), .158, 1e-3 );
+%! assert( R(1,1), .158, 1e-3 ); # modified from the original example, as the reference above considers R as the residence time, not the response time
+%! assert( Q(1,1), .25, 1e-2 );
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnopensingle.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,217 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnopensingle (@var{lambda}, @var{S}, @var{V}) 
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnopensingle (@var{lambda}, @var{S}, @var{V}, @var{m})
+##
+## @cindex open network, single class
+## @cindex BCMP network
+##
+## Analyze open, single class BCMP queueing networks.
+##
+## This function works for a subset of BCMP single-class open networks
+## satisfying the following properties:
+##
+## @itemize
+##
+## @item The allowed service disciplines at network nodes are: FCFS,
+## PS, LCFS-PR, IS (infinite server);
+##
+## @item Service times are exponentially distributed and
+## load-independent; 
+##
+## @item Service center @math{i} can consist of @code{@var{m}(i) @geq{} 1} 
+## identical servers.
+##
+## @item Routing is load-independent
+##
+## @end itemize
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Overall external arrival rate (@code{@var{lambda}>0}).
+##
+## @item S
+## @code{@var{S}(k)} is the average service time at center
+## @math{i} (@code{@var{S}(k)>0}).
+##
+## @item V
+## @code{@var{V}(k)} is the average number of visits to center
+## @math{k} (@code{@var{V}(k) @geq{} 0}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center @math{i}. If
+## @code{@var{m}(k) < 1}, then service center @math{k} is a delay center
+## (IS); otherwise it is a regular queueing center with
+## @code{@var{m}(k)} servers. Default is @code{@var{m}(k) = 1} for each
+## @math{k}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## If @math{k} is a queueing center, 
+## @code{@var{U}(k)} is the utilization of center @math{k}.
+## If @math{k} is an IS node, then @code{@var{U}(k)} is the
+## @emph{traffic intensity} defined as @code{@var{X}(k)*@var{S}(k)}.
+##
+## @item R
+## @code{@var{R}(k)} is the average response time of center @math{k}.
+##
+## @item Q
+## @code{@var{Q}(k)} is the average number of requests at center
+## @math{k}.
+##
+## @item X
+## @code{@var{X}(k)} is the throughput of center @math{k}.
+##
+## @end table
+##
+## @seealso{qnopen,qnclosed,qnvisits}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnopensingle( lambda, S, V, m )
+  if ( nargin < 3 || nargin > 4 )
+    print_usage();
+  endif
+  ( isscalar(lambda) && lambda>0 ) || \
+      usage( "lambda must be a positive number" );
+  lambda = lambda(:)';
+  ( isvector( S ) && all(S>0) ) || \
+      usage( "S must be a vector >0" );
+  S = S(:)';
+  K = length(S);
+  ( isvector( V ) && length(V)==K && all(V>=0) ) || \
+      usage( "V must be a vector >=0 and of the same length as S" );
+  V = V(:)';
+
+  if ( nargin < 4 )
+    m = ones(1,K);
+  else
+    (isvector(m) && (length(m) == K)) || \
+	usage( "m must be a vector of %d elements", K);
+    m = m(:)';
+    [err m] = common_size(m,S);
+    ( err == 0 ) || \
+        usage( "m and S are not of common size" );
+  endif
+
+  ## Compute maximum processing capacity
+  lambda_sat = 1 / max( S.* V );
+  (lambda <= lambda_sat) || \
+      error( "Processing capacity exceeded (lambda must be less than %f)", lambda_sat );
+
+  l = lambda*V; # arrival rates
+
+  i = find( m == 1 ); # single station queueing centers
+  if numel(i)
+    [U(i) R(i) Q(i) X(i)] = qnmm1( l(i), 1./S(i) );
+  endif
+  
+  i = find( m<1 ); # delay centers
+  if numel(i)
+    [U(i) R(i) Q(i) X(i)] = qnmminf( l(i), 1./S(i) );
+  endif
+  
+  i = find( m>1 ); # multiple stations queueing centers
+  if numel(i)
+    [U(i) R(i) Q(i) X(i)] = qnmmm( l(i), 1./S(i), m(i) );
+  endif
+endfunction
+%!test
+%! lambda = 0;
+%! S = [1 1 1];
+%! V = [1 1 1];
+%! fail( "qnopensingle(lambda,S,V)","lambda must be");
+%! lambda = 1;
+%! S = [1 0 1];
+%! fail( "qnopensingle(lambda,S,V)","S must be");
+%! S = [1 1 1];
+%! m = [1 1];
+%! fail( "qnopensingle(lambda,S,V,m)","m must be a vector");
+%! V = [1 1 1 1];
+%! fail( "qnopensingle(lambda,S,V)","same length as S");
+ 
+%!test
+%! # Example 34.1 p. 572 Bolch et al.
+%! lambda = 3;
+%! V = [16 7 8];
+%! S = [0.01 0.02 0.03];
+%! [U R Q X] = qnopensingle( lambda, S, V );
+%! assert( R, [0.0192 0.0345 0.107], 1e-2 );
+%! assert( U, [0.48 0.42 0.72], 1e-2 );
+
+%!test
+%! # Example p. 113, Lazowska et al.
+%! V = [121 70 50];
+%! S = [0.005 0.03 0.027];
+%! lambda=0.3;
+%! [U R Q X] = qnopensingle( lambda, S, V );
+%! assert( U(1), 0.182, 1e-3 );
+%! assert( X(1), 36.3, 1e-2 );
+%! assert( Q(1), 0.222, 1e-3 );
+
+%!test
+%! # Compare the results of this function with qnjackson
+%! P = [ 0 0.4 0.6 0; ...
+%!       0.2 0 0.2 0.6; ...
+%!       0 0 0 1; ...
+%!       0 0 0 0 ];
+%! lambda = [0.1 0 0 0.3];
+%! V = qnvisits(P,lambda);
+%! S = [2 1 2 1.8];
+%! m = [3 1 1 2];
+%! [U R Q X] = qnopensingle( sum(lambda), S, V, m );
+%! [U_j R_j Q_j X_j] = qnjackson( lambda, S, P, m );
+%! assert( U, U_j, 1e-4 );
+%! assert( R, R_j, 1e-4 );
+%! assert( Q, Q_j, 1e-4 );
+%! assert( X, X_j, 1e-4 );
+
+%!test
+%! lambda=[1];
+%! P=[0];
+%! V=qnvisits(P,lambda);
+%! S=[0.25];
+%! [U1 R1 Q1 X1]=qnopensingle(sum(lambda),S,V); 
+%! [U2 R2 Q2 X2]=qnmm1(lambda(1),1/S(1));
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 1e-5 );
+%! assert( X1, X2, 1e-5 );
+
+
+%!demo
+%! lambda = 3;
+%! V = [16 7 8];
+%! S = [0.01 0.02 0.03];
+%! [U R Q X] = qnopensingle( lambda, S, V );
+%! R_s = dot(R,V) # System response time
+%! N = sum(Q) # Average number in system
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnsolve.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,771 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnsolve (@var{"closed"}, @var{N}, @var{QQ}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnsolve (@var{"closed"}, @var{N}, @var{QQ}, @var{V}, @var{Z})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnsolve (@var{"open"}, @var{lambda}, @var{QQ}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnsolve (@var{"mixed"}, @var{lambda}, @var{N}, @var{QQ}, @var{V})
+##
+## General evaluator of QN models. Networks can be open,
+## closed or mixed; single as well as multiclass networks are supported.
+##
+## @itemize
+##
+## @item For @strong{closed} networks, the following server types are
+## supported: @math{M/M/m}--FCFS, @math{-/G/\infty}, @math{-/G/1}--LCFS-PR,
+## @math{-/G/1}--PS and load-dependent variants.
+##
+## @item For @strong{open} networks, the following server types are supported:
+## @math{M/M/m}--FCFS, @math{-/G/\infty} and @math{-/G/1}--PS. General
+## load-dependent nodes are @emph{not} supported. Multiclass open networks
+## do not support multiple server @math{M/M/m} nodes, but only
+## single server @math{M/M/1}--FCFS.
+##
+## @item For @strong{mixed} networks, the following server types are supported:
+## @math{M/M/1}--FCFS, @math{-/G/\infty} and @math{-/G/1}--PS. General
+## load-dependent nodes are @emph{not} supported.
+##
+## @end itemize
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## Number of requests in the system for closed networks. For
+## single-class networks, @var{N} must be a scalar. For multiclass
+## networks, @code{@var{N}(c)} is the population size of closed class
+## @math{c}.
+##
+## @item lambda
+## External arrival rate (scalar) for open networks. For single-class
+## networks, @var{lambda} must be a scalar. For multiclass networks,
+## @code{@var{lambda}(c)} is the class @math{c} overall arrival rate.
+##
+## @item QQ
+## List of queues in the network. This must be a cell array 
+## with @math{N} elements, such that @code{@var{QQ}@{i@}} is
+## a struct produced by the @code{qnmknode} function.
+##
+## @item Z
+## External delay ("think time") for closed networks. Default 0.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## If @math{i} is a FCFS node, then @code{@var{U}(i)} is the utilization
+## of service center @math{i}. If @math{i} is an IS node, then
+## @code{@var{U}(i)} is the @emph{traffic intensity} defined as
+## @code{@var{X}(i)*@var{S}(i)}.
+##
+## @item R
+## @code{@var{R}(i)} is the average response time of service center @math{i}.
+##
+## @item Q
+## @code{@var{Q}(i)} is the average number of customers in service center
+## @math{i}.
+##
+## @item X
+## @code{@var{X}(i)} is the throughput of service center @math{i}.
+##
+## @end table
+##
+## Note that for multiclass networks, the computed results are per-class
+## utilization, response time, number of customers and throughput:
+## @code{@var{U}(c,k)}, @code{@var{R}(c,k)}, @code{@var{Q}(c,k)},
+## @code{@var{X}(c,k)},
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnsolve( network_type, varargin )
+  if ( nargin < 2 )
+    print_usage();
+  endif
+
+  ischar(network_type) || \
+      usage("First parameter must be a string");
+
+  network_type = tolower(network_type);
+
+  if ( strcmp(network_type, "open" ) )
+    [U R Q X] = __qnsolve_open( varargin{:} );
+  elseif ( strcmp(network_type, "closed" ) )
+    [U R Q X] = __qnsolve_closed( varargin{:} );
+  elseif (strcmp(network_type, "mixed" ) )
+    [U R Q X] = __qnsolve_mixed( varargin{:} );
+  else
+    usage( "Invalid network type %s: must be one of \"open\", \"closed\" or \"mixed\"", network_type );
+  endif
+endfunction
+
+##############################################################################
+## Dispatcher function for open networks
+function [U R Q X] = __qnsolve_open( lambda, varargin )
+  if ( isscalar(lambda) )
+    [U R Q X] = __qnsolve_open_single( lambda, varargin{:} );
+  else
+    [U R Q X] = __qnsolve_open_multi( lambda, varargin{:} );
+  endif
+endfunction
+
+##############################################################################
+## Worker function for open, single class networks
+function [U R Q X] = __qnsolve_open_single( lambda, QQ, V )
+
+  if ( nargin != 3 )
+    print_usage();
+  endif
+
+  ( isscalar(lambda) && (lambda>0) ) || \
+      usage( "lambda must be a scalar > 0" );
+  
+  iscell(QQ) || \
+      usage( "QQ must be a cell array" );
+
+  N = length(QQ);
+
+  ( isvector(V) && length(V) == N ) || \
+      usage( "V must be a vector of length %d", N );
+
+  V = V(:); # make V a row vector
+  all(V>=0) || \
+      usage( "V must be >= 0" );
+
+  ## Initialize vectors
+  S = zeros(size(V));
+  m = ones(size(V));
+  for i=1:N
+    QQ{i}.c == 1 || \
+	usage( "Multiclass networks are not supported by this function" );
+    S(i) = QQ{i}.S;
+    if __is_li(QQ{i})
+      ; # nothing to do
+    elseif __is_multi(QQ{i})
+      m(i) = QQ{i}.m;
+    elseif __is_is(QQ{i})
+      m(i) = -1;
+    else
+      usage( "Unsupported type \"%s\" for node %d", QQ{i}.node, i );
+    endif
+  endfor
+
+  [U R Q X] = qnopensingle( lambda, S, V, m );
+  __prettyprint( 0, lambda, QQ, V, U, R, Q, X );
+endfunction
+
+
+##############################################################################
+## Worker function for open, multiclass networks
+function [U R Q X] = __qnsolve_open_multi( lambda, QQ, V )
+  if ( nargin != 3 )
+    print_usage();
+  endif
+  isvector(lambda) && all(lambda > 0) || \
+      usage( "lambda must be a vector >0" );
+  lambda = lambda(:)'; # make lambda a row vector
+  iscell(QQ) || \
+      usage( "QQ must be a cell array" );
+  C = length(lambda);
+  K = length(QQ);
+  [C,K] == size(V) || \
+      usage( "V size mismatch" );
+  all( all( V>= 0 ) ) || \
+      usage( "V must be >= 0 " );
+
+  S = zeros(C,K);
+  m = ones(1,K);
+  for i=1:K
+    QQ{i}.c == C || \
+	usage( "Wrong number of classes for center %d (is %d, should be %d)", i, QQ{i}.c, C );
+    S(:,i) = QQ{i}.S(:);
+    if __is_li(QQ{i})
+      ; # nothing to do
+    elseif __is_is(QQ{i})
+      m(i) = -1;
+    else
+      usage( "Unsupported type \"%s\" for node %d", QQ{i}.node, i );
+    endif  
+  endfor
+
+  [U R Q X] = qnopenmulti( lambda, S, V, m );
+  __prettyprint( 0, lambda, QQ, V, U, R, Q, X );
+endfunction
+
+
+##############################################################################
+## Dispatcher function for closed networks
+function [U R Q X] = __qnsolve_closed( N, varargin )
+  if ( isscalar(N) )
+    [U R Q X] = __qnsolve_closed_single( N, varargin{:} );
+  else
+    [U R Q X] = __qnsolve_closed_multi( N, varargin{:} );
+  endif
+endfunction
+
+
+##############################################################################
+## Worker function for closed, single-class networks
+function [U R Q X] = __qnsolve_closed_single( N, QQ, V, Z )
+
+  if ( nargin < 3 || nargin > 4 )
+    usage();
+  endif
+
+  isscalar(N) || \
+      usage( "Multiclass networks are not supported by this function" );
+
+  iscell(QQ) || \
+      usage( "QQ must be a cell array" );
+
+  if ( nargin < 4 ) 
+    Z = 0;
+  else
+    isscalar(Z) && Z >= 0 || \
+        usage( "Z must be >= 0" );
+  endif
+
+  K = length(QQ);
+  
+  ( isvector(V) && length(V) == K ) || \
+      usage( "V must be a vector of length %d", K );
+
+  ## Initialize vectors
+  i_single = i_multi = i_delay = i_ld = [];
+  for i=1:K
+    ( QQ{i}.c == 1 ) || \
+	usage( "Multiclass networks are not supported by this function" );
+    if __is_li(QQ{i})
+      i_single = [i_single i];
+    elseif __is_multi(QQ{i})
+      i_multi = [i_multi i];
+    elseif __is_is(QQ{i})
+      i_delay = [i_delay i];
+    elseif __is_ld(QQ{i})
+      i_ld = [i_ld i];
+    else
+      usage( "Unsupported type \"%s\" for node %d", QQ{i}.node, i );
+    endif
+  endfor
+  p = cell( 1, K );
+
+  for i=i_multi
+    p{i} = zeros(1,QQ{i}.m+1); # p(i,j+1) is the probability that there are j jobs at server i
+    p{i}(1) = 1;
+  endfor
+
+  for i=i_ld
+    p{i} = zeros(1,N+1); # p(i,j+1) is the probability that there are j jobs at server i
+    p{i}(1) = 1;
+  endfor
+
+  U = R = Q = X = zeros( 1, K );
+  ## Trivial case of empty population: just return all zeros
+  if ( N == 0 )
+    return;
+  endif
+  X_s = 0;              # System throughput
+
+  ## Main MVA loop, iterates over the population size
+  for n=1:N 
+
+    ## Single server nodes
+    for i=i_single
+      R(i) = QQ{i}.S .* (1 + Q(i)); 
+    endfor
+
+    ## Multiple server nodes
+    for i=i_multi
+      j=0:QQ{i}.m-2;
+      R(i) = QQ{i}.S / QQ{i}.m * (1+Q(i)+dot( QQ{i}.m-j-1, p{i}( 1+j ) ) );
+    endfor
+
+    ## General load-dependent nodes
+    for i=i_ld
+      j=1:n;
+      R(i) = sum( j.*QQ{i}.S(j).*p{i}(j) );
+    endfor
+    
+    ## Delay centers (IS)
+    for i=i_delay
+      R(i) = QQ{i}.S;
+    endfor
+
+    R_s = dot( V, R ); # System response time
+    X_s = n / ( Z + R_s ); # System Throughput
+    Q = X_s * ( V .* R );
+
+    ## prepare for next iteration
+    lambda_i = V * X_s; # lambda_i(i) is the node i throughput
+
+    ## Update probabilities for multiple server nodes
+    for i=i_multi
+      j=1:QQ{i}.m-1; # range
+      p{i}(j+1) = lambda_i(i) .* QQ{i}.S ./ min( j,QQ{i}.m ) .* p{i}(j);
+      p{i}(1) = 1 - 1/QQ{i}.m * ...
+          (V(i)*QQ{i}.S*X_s + dot( QQ{i}.m-j, p{i}(j+1)) );
+    endfor
+
+    ## Update probabilities for load-dependent nodes
+    for i=i_ld
+      j=1:n;
+      p{i}(1+j) = X_s * QQ{i}.S(j) .* p{i}(j) * V(i);      
+      p{i}(1) = 1-sum(p{i}(1+j));
+    endfor    
+
+  endfor
+  X = X_s * V; # Service centers throughput
+
+  ## Single server or IS nodes
+  for i=[i_single i_delay]
+    U(i) = X(i) .* QQ{i}.S;
+  endfor
+
+  ## Multiple server nodes
+  for i=i_multi
+    U(i) = X(i) .* QQ{i}.S ./ QQ{i}.m;
+  endfor
+
+  ## General load-dependent nodes
+  for i=i_ld
+    U(i) = 1-p{i}(1);
+  endfor
+
+  __prettyprint( N, 0, QQ, V, U, R, Q, X );
+endfunction
+
+##############################################################################
+## Worker function for closed, multi-class networks
+function [U R Q X] = __qnsolve_closed_multi( N, QQ, V, Z )
+
+  if ( nargin < 3 || nargin > 4 )
+    print_usage();
+  endif
+
+  isvector(N) && all( N>0 ) || \
+      usage( "N must be >0" );
+
+  iscell(QQ) || \
+      usage( "QQ must be a cell array" );
+
+  C = length(N); ## Number of classes
+  K = length(QQ); ## Number of service centers
+  size(V) == [C,K] || \
+      usage( "V size mismatch" );
+
+  if ( nargin < 4 )
+    Z = zeros(1,C);
+  else
+    isvector(Z) && length(Z) == C || \
+	usage( "Z size mismatch" );
+  endif
+
+  ## Check consistence of parameters
+  all( all( V >= 0 ) ) || \
+      usage( "V must be >=0" );
+
+  ## Initialize vectors
+  i_single = i_multi = i_delay = i_ld = [];
+  S = zeros(C,K);
+  for i=1:K
+    ( QQ{i}.c == C ) || \
+	usage( "Service center %d has wrong number of classes (is %d, should be %d)", i, QQ{i}.c, C );
+
+    if __is_li(QQ{i})
+      i_single = [i_single i];
+      ( !strcmpi( QQ{i}.node, "m/m/m-fcfs" ) || all( QQ{i}.S(1) == QQ{i}.S )) || \
+	  usage( "Service times at FIFO node %d are not class-independent", i );
+    elseif __is_multi(QQ{i})
+      i_multi = [i_multi i];
+    elseif __is_is(QQ{i})
+      i_delay = [i_delay i];
+    elseif __is_ld(QQ{i})
+      columns( QQ{i}.S ) == sum(N) || \
+	  usage( "Load-dependent center %d has insufficient data (is %d, should be %d", i, columns(QQ{i}.S), sum(N) );
+      i_ld = [i_ld i];
+    else
+      usage( "Unknown or unsupported type \"%s\" for node %d", QQ{i}.node, i );
+    endif
+  endfor
+
+  ## Initialize results
+  U = R = zeros( C, K );
+  X = zeros( 1, C );
+  Q_next = Q = sparse( prod(N+1),K );
+  p = cell(1,K);
+  for k=i_multi
+    ## p{i}(j+1,k+1) is the probability to have j jobs at node i
+    ## where the network is in state k
+    p{k} = zeros( QQ{k}.m+1,prod(N+1) );
+    p{k}(1,__get_idx( N, 0*N )) = 1;
+  endfor
+
+  for k=i_ld
+    ## p{i}(j+1,k+1) is the probability to have j jobs at node i
+    ## where the network is in state k
+    p{k} = zeros( columns(QQ{k}.S )+1, prod(N+1) );
+    p{k}(1,__get_idx( N, 0*N )) = 1;
+  endfor
+  
+  ## Main loop
+  for n=1:sum(N)
+    feasible_set = population_mix( n, N );
+    for nn=1:rows(feasible_set)
+      n_bar = feasible_set(nn,:);
+      for c=1:C
+	if ( n_bar(c) > 0 )
+
+	  ## single server nodes
+          for k=i_single
+            n_bar_c = __minusonec(n_bar,c);
+            idx = __get_idx( N, n_bar_c );
+            R(c,k) = QQ{k}.S(c)*(1 + Q( idx, k ) );  
+            ## for FCFS nodes with class-dependent service times,
+            ## it is possible to use the following approximation
+            ## (p. 469 Bolch et al.)
+            ##
+            ## R(c,k) = S(c,k) + sum( S(:,k) * Q(idx(:), k) );
+	  endfor
+	  
+	  ## multi server nodes
+	  for k=i_multi
+            n_bar_c = __minusonec(n_bar,c);
+            idx = __get_idx( N, n_bar_c );
+            j=0:QQ{k}.m-2; # range
+            R(c,k) = QQ{k}.S(c)/QQ{k}.m*(1 + Q( idx, k ) + ...
+					 dot(QQ{k}.m-j-1,p{k}(j+1,idx) ) );
+	  endfor
+
+	  ## General load-dependent nodes
+	  for k=i_ld
+            n_bar_c = __minusonec(n_bar,c);
+            idx = __get_idx( N, n_bar_c );
+            j=1:sum(n_bar); # range
+            R(c,k) = sum( j .* QQ{k}.S(c,j) .* p{k}(j,idx)' );
+	  endfor
+	endif
+
+	## delay centers
+	for k=i_delay
+          R(c,k) = QQ{k}.S(c);
+        endfor
+ 
+      endfor # c
+      X = n_bar ./ ( Z .+ dot(R,V,2)' ); # X(c) = N(c) / ( Z(c) + sum_k R(c,k) * V(c,k) )
+
+      idx = __get_idx( N, n_bar );
+      ## Q_k = sum_c X(c) * R(c,k)
+      for k=1:K
+        Q_next( idx, k ) = dot( X, R(:,k) .* V(:,k) );
+      endfor
+
+      ## Adjust probabilities for multiple server nodes
+      for k=i_multi
+        s=0; # it is actually a vector
+        j=1:QQ{k}.m-1;
+        for r=find(n_bar>0) # I don't know how to vectorize this
+          ii = __minusonec(n_bar,r);
+          s+=QQ{k}.S(r)*V(r,k)*X(r)*p{k}(j,__get_idx(N,ii));
+        endfor
+        p{k}(j+1,idx) = s./j;
+        p{k}(1,idx) = 1-1/QQ{k}.m*(sum( QQ{k}.S(:) .* V(:,k) .* X(:) ) + ...
+                                   dot( QQ{k}.m-j, p{k}(j+1,idx) ) );
+      endfor
+
+      ## Adjust probabilities for general load-dependent server nodes
+      for k=i_ld
+        s=0; # it is actually a vector
+        j=1:sum(n_bar);
+        for r=find(n_bar>0)
+          ii = __minusonec(n_bar,r);
+          s+=QQ{k}.S(r,sum(n_bar))*V(r,k)*X(r)*p{k}(j,__get_idx(N,ii));
+        endfor
+        p{k}(j+1,idx) = s;
+        p{k}(1,idx) = 1-sum(p{k}(1+j,idx));	  
+      endfor
+    endfor
+    Q = Q_next;
+    Q_next = sparse( prod(N+1), K );
+  endfor
+  for k=1:K
+    if __is_ld(QQ{k})
+      U(:,k) = 1-p{k}(1, __get_idx(N,N));
+    else
+      U(:,k) = X(:) .* QQ{k}.S(:) .* V(:,k); # U(c,k) = X(c)*D(c,k)
+    endif
+  endfor
+  Q = (diag(X)*R).*V; # dmult(X,R).*V;
+  X = diag(X)*V; # dmult(X,V);
+endfunction
+
+##############################################################################
+## Compute the linear index corresponding to vector i from a population
+## of N.
+function idx = __get_idx( N, i )
+  i_cell = num2cell( i+1 );
+  idx = sub2ind( N+1, i_cell{:} );
+endfunction
+
+##############################################################################
+## Given an input vector n, returns an output vector r which is equal to
+## n except that the element at the c-th position is decreased by one:
+## r(c) = n(c)-1. Warning: no check is made on the parameters
+function r = __minusonec( n, c )
+  r = n; r(c) -= 1;
+endfunction
+
+
+##############################################################################
+## Worker function for mixed networks. This function delegates to qnmix
+function [U R Q X] = __qnsolve_mixed( lambda, N, QQ, V )
+  if ( nargin != 4 )
+    print_usage();
+  endif
+  ( isvector(lambda) && isvector(N) && size_equal(lambda,N) ) || \
+      usage( "lambda and N must be vectors of the same size" );
+  ( iscell(QQ) && length(QQ) == length(lambda) ) || \
+      usage( "QQ size mismatch (is %d, should be %d)", length(QQ), length(lambda) );
+
+  C = length(lambda); # number of classes
+  K = length(QQ); # number of service centers
+  S = zeros(C,K);
+  m = ones(1,K);
+  ## fill S matrix
+  for k=1:K
+    if __is_ld(QQ{k})
+      usage( "General load-dependent service center %d is not supported", k );
+    elseif __is_is(QQ{k})
+      m(k) = -1;
+    else
+      m(k) = QQ{k}.m;
+    endif
+    S(:,k) = QQ{k}.S;
+  endfor
+  [U R Q X] = qnmix( lambda, N, S, V, m );
+  __prettyprint( N, lambda, QQ, V, U, R, Q, X );
+endfunction
+
+##############################################################################
+## return true iff Q is an infinite server (IS) node
+function result = __is_is( Q )
+  result = strcmp(Q.node, "-/g/inf" );
+endfunction
+
+##############################################################################
+## return true iff Q is a multi-server FIFO node
+function result = __is_multi( Q )
+  result =  (strcmp(Q.node, "m/m/m-fcfs") && Q.m>1);
+endfunction
+
+##############################################################################
+## return true iff Q is a single-server, load-dependent node
+function result = __is_ld( Q )
+  result = ( (strcmp(Q.node, "m/m/m-fcfs") || \
+	      strcmp(Q.node, "-/g/1-lcfs-pr") || \
+	      strcmp(Q.node, "-/g/1-ps" ) ) && \
+	    columns( Q.S ) > 1 );
+endfunction
+
+##############################################################################
+## return ture iff Q is a single-server, load-independent node
+function result = __is_li( Q )
+  result = ((Q.m==1) && (1 == columns( Q.S )) && !strcmp( Q.node, "-/g/inf" ) );
+endfunction
+
+##############################################################################
+## This function is used to "pretty-print" a solved network. Used for
+## debugging
+function __prettyprint( N, lambda, QQ, V, U, R, Q, X )
+  return; ## immediately return
+  [errorcode, N, lambda] = common_size( N, lambda );
+  if ( errorcode)
+    usage( "N and lambda are of incompatible size" );
+  endif
+  ( isvector(N) && isvector(lambda) && size_equal(lambda,N) ) || \
+      usage( "N and lambda must be vector of the same length" );
+  C = length(N);  
+  K = length(QQ); # number of service centers
+
+  [C,K] == size(V) || \
+      usage( "V size mismatch" );
+  [C,K] == size(U) || \
+      usage( "U size mismatch" );
+  [C,K] == size(R) || \
+      usage( "R size mismatch" );
+  [C,K] == size(Q) || \
+      usage( "Q size mismatch" );
+  [C,K] == size(X) || \
+      usage( "X size mismatch" );
+
+  for c=1:C     
+    printf("\n");
+    printf("=== CLASS %d ===\n", c );
+    if ( N(c)>0 )
+      printf("Type: CLOSED\nPopulation: %d\n", N(c))
+    else
+      printf("Type: OPEN\nRequests arrival rate: %6.2f\n", lambda(c))
+    endif
+    printf("\n");
+    printf("+---+---------------+---+------+------+------+------+------+------+\n");
+    printf("| i |     Node type | m | S(i) | V(i) | U(i) | R(i) | Q(i) | X(i) |\n");
+    printf("+---+---------------+---+------+------+------+------+------+------+\n");
+    for i=1:K
+      if ( isscalar(QQ{i}.S(c)) )
+	serv = sprintf("%6.2f",QQ{i}.S(c));
+      else
+	serv = "LD";
+      endif
+      printf("|%3d|%-33s|      |      |      |      |\n", i, QQ{i}.comment);
+      printf("|   |%15s|%3d|%6s|%6.2f|%6.4f|%6.2f|%6.2f|%6.2f|\n",
+	     QQ{i}.node, QQ{i}.m, serv, V(c,i), U(c,i), R(c,i), Q(c,i), X(c,i) ); 
+      
+    endfor
+    printf("+---+---------------+---+------+------+------+------+------+------+\n");
+    printf("|               THIS CLASS STATISTICS | ---- |%6.2f|%6.2f|%6.2f|\n",
+	   dot(R(c,:),V(c,:)), sum(Q(c,:)), X(c,1)/V(c,1) );
+    printf("+---+---------------+---+------+------+------+------+------+------+\n\n");
+  endfor
+endfunction
+
+%!test
+%! # Example 8.7 p. 349 Bolch et al.
+%! N = 3;
+%! Q1 = qnmknode( "m/m/m-fcfs", .5, 2 );
+%! Q2 = qnmknode( "m/m/m-fcfs", 1/1.667 );
+%! Q3 = qnmknode( "m/m/m-fcfs", 1/1.25 );
+%! Q4 = qnmknode( "m/m/m-fcfs", 1./[1 2 3] );
+%! V = [ 1 .5 .5 1 ];
+%! [U R Q X] = qnsolve("closed",N, { Q1, Q2, Q3, Q4 }, V);
+%! assert( Q, [0.624 0.473 0.686 1.217], 1e-3 );
+%! assert( R, [0.512 0.776 1.127 1], 1e-3 );
+
+%!test
+%! # Example 8.7 p. 349 Bolch et al.
+%! N = 3;
+%! Q1 = qnmknode( "m/m/m-fcfs", 1/2, 2 );
+%! Q2 = qnmknode( "m/m/m-fcfs", 1/1.667 );
+%! Q3 = qnmknode( "m/m/m-fcfs", 1/1.25 );
+%! Q4 = qnmknode( "-/g/inf", 1 );
+%! V = [ 1 .5 .5 1 ];
+%! [U R Q X] = qnsolve("closed",N, { Q1, Q2, Q3, Q4 }, V);
+%! assert( Q, [0.624 0.473 0.686 1.217], 1e-3 );
+%! assert( R, [0.512 0.776 1.127 1], 1e-3 );
+
+%!test
+%! # Example 8.4 p. 333 Bolch et al.
+%! N = 3;
+%! Q1 = qnmknode( "m/m/m-fcfs", .5, 2 );
+%! Q2 = qnmknode( "m/m/m-fcfs", .6 );
+%! Q3 = qnmknode( "m/m/m-fcfs", .8 );
+%! Q4 = qnmknode( "-/g/inf", 1 );
+%! V = [ 1 .5 .5 1 ];
+%! [U R Q X] = qnsolve("closed",N, { Q1, Q2, Q3, Q4 }, V);
+%! assert( U(1:3), [.304 .365 .487], 1e-3 );
+%! assert( X, [1.218 0.609 0.609 1.218], 1e-3 );
+
+%!test
+%! # Same as above, with center 1 replaced with a load-dependent service center
+%! N = 3;
+%! Q1 = qnmknode( "m/m/m-fcfs", [.5 .25 .25] );
+%! Q2 = qnmknode( "m/m/m-fcfs", .6 );
+%! Q3 = qnmknode( "m/m/m-fcfs", .8 );
+%! Q4 = qnmknode( "m/m/m-fcfs", [1 1/2 1/3] );
+%! V = [ 1 .5 .5 1 ];
+%! [U R Q X] = qnsolve("closed",N, { Q1, Q2, Q3, Q4 }, V);
+%! assert( U(2:3), [.365 .487], 1e-3 ); ## NOTE: Utilization U(1) is computed differently from M/M/m nodes and load-dependent M/M/1 nodes
+%! assert( X, [1.218 0.609 0.609 1.218], 1e-3 );
+
+%!test
+%! # Example 7.4 p. 287 Bolch et al.
+%! QQ = { qnmknode( "m/m/m-fcfs", 0.04 ), \
+%!        qnmknode( "m/m/m-fcfs", 0.03 ), \
+%!        qnmknode( "m/m/m-fcfs", 0.06 ), \
+%!        qnmknode( "m/m/m-fcfs", 0.05 ) };
+%! P = [ 0 0.5 0.5 0; 1 0 0 0; 0.6 0 0 0; 1 0 0 0 ];
+%! lambda = [0 0 0 4];
+%! [U R Q X] = qnsolve("open", sum(lambda), QQ, qnvisits(P,lambda) );
+%! assert( X, [20 10 10 4], 1e-4 );
+%! assert( U, [0.8 0.3 0.6 0.2], 1e-2 );
+%! assert( R, [0.2 0.043 0.15 0.0625], 1e-3 );
+%! assert( Q, [4, 0.429 1.5 0.25], 1e-3 );
+
+%!test
+%! V = [1 1; 1 1];
+%! Q1 = qnmknode( "m/m/m-fcfs", [1;2] );
+%! Q2 = qnmknode( "m/m/m-fcfs", [3;4] );
+%! lambda = [3/19 2/19];
+%! [U R Q] = qnsolve("open", lambda, { Q1, Q2 }, V);
+%! assert( U(1,1), 3/19, 1e-6 );
+%! assert( U(2,1), 4/19, 1e-6 );
+%! assert( R(1,1), 19/12, 1e-6 );
+%! assert( R(1,2), 57/2, 1e-6 );
+%! assert( Q(1,1), .25, 1e-6 );
+
+## Example 9.5 p. 337, Bolch et al.
+%!test
+%! QQ = { qnmknode( "m/m/m-fcfs", [0.2; 0.2], 2 ), \
+%!        qnmknode( "-/g/1-ps", [0.4; 0.6] ), \
+%!        qnmknode( "-/g/inf", [1; 2] ) };
+%! V = [ 1 0.6 0.4; 1 0.3 0.7 ];
+%! N = [ 2 1 ];
+%! [U R Q X] = qnsolve( "closed", N, QQ, V );
+%! assert( Q, [ 0.428 0.726 0.845; 0.108 0.158 0.734 ], 1e-3 );
+%! assert( X(1,1), 2.113, 1e-3 ); # CHECK
+%! assert( X(2,1), 0.524, 1e-3 ); # CHECK
+%! assert( all( all(U(:,[1,2])<=1) ) );
+
+## Same as above, but with general load-dependent centers
+%!test
+%! QQ = { qnmknode( "m/m/m-fcfs", [0.2 0.1 0.1; 0.2 0.1 0.1] ), \
+%!        qnmknode( "-/g/1-ps", [0.4; 0.6] ), \
+%!        qnmknode( "-/g/inf", [1; 2] ) };
+%! V = [ 1 0.6 0.4; 1 0.3 0.7 ];
+%! N = [ 2 1 ];
+%! [U R Q X] = qnsolve( "closed", N, QQ, V );
+%! assert( Q, [ 0.428 0.726 0.845; 0.108 0.158 0.734 ], 1e-3 );
+%! assert( X(1,1), 2.113, 1e-3 ); # CHECK
+%! assert( X(2,1), 0.524, 1e-3 ); # CHECK
+%! assert( all( all(U(:,[1,2])<=1) ) );
+
+%!test
+%! # example p. 26 Schwetman
+%! QQ = { qnmknode( "m/m/m-fcfs", [.25; .25] ),
+%!        qnmknode( "-/g/1-ps", [0; .1] ) };
+%! V = [1 0; 1 1];
+%! lambda = [1 0];
+%! N = [0 3];
+%! [U R Q X] = qnsolve( "mixed", lambda, N, QQ, V );
+%! assert( U(1,1), .25, 1e-3 );
+%! assert( X(1,1), 1.0, 1e-3 );
+%! assert( [R(1,1) R(2,1) R(2,2)], [1.201 0.885 0.135], 1e-3 );
+
+%!demo
+%! QQ = { qnmknode( "m/m/m-fcfs", [0.2 0.1 0.1; 0.2 0.1 0.1] ), \
+%!        qnmknode( "-/g/1-ps", [0.4; 0.6] ), \
+%!        qnmknode( "-/g/inf", [1; 2] ) };
+%! V = [ 1 0.6 0.4; \
+%!       1 0.3 0.7 ];
+%! N = [ 2 1 ];
+%! [U R Q X] = qnsolve( "closed", N, QQ, V );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/inst/qnvisits.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,488 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox 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 3 of the
+## License, or (at your option) any later version.
+##
+## The queueing toolbox 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with the queueing toolbox. If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+##
+## @deftypefn {Function File} {[@var{V} @var{ch}] =} qnvisits (@var{P})
+## @deftypefnx {Function File} {@var{V} =} qnvisits (@var{P}, @var{lambda})
+##
+## Compute the average number of visits to the service centers of a
+## single class, open or closed Queueing Network with @math{N} service
+## centers.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## Routing probability matrix. For single class networks,
+## @code{@var{P}(i,j)} is the probability that a request which completed
+## service at center @math{i} is routed to center @math{j}. For closed
+## networks it must hold that @code{sum(@var{P},2)==1}. The routing
+## graph myst be strongly connected, meaning that it must be possible to
+## eventually reach each node starting from each node. For multiple
+## class networks, @code{@var{P}(r,i,s,j)} is the probability that a
+## class @math{r} request which completed service at center @math{i} is
+## routed to center @math{j} as a class @math{s} request. Class switching
+## is supported.
+##
+## @item lambda
+## (open networks only) vector of external arrivals. For single class
+## networks, @code{@var{lambda}(i)} is the external arrival rate to
+## center @math{i}. For multiple class networks,
+## @code{@var{lambda}(r,i)} is the arrival rate of class @math{r}
+## requests to center @math{i}. If this parameter is omitted, the
+## network is assumed to be closed.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item V
+## For single class networks, @code{@var{V}(i)} is the average number of
+## visits to server @math{i}. For multiple class networks,
+## @code{@var{V}(r,i)} is the class @math{r} visit ratio at center
+## @math{i}.
+##
+## @item ch
+## (For closed networks only). @code{@var{ch}(c,k)} is the
+## number of the chain that class @math{c} at center @math{k} belongs to. 
+## The total number of chains is @code{max(@var{ch})}. 
+##
+## @end table
+## 
+## @end deftypefn
+
+## Author: Moreno Marzolla <marzolla(at)cs.unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [V ch] = qnvisits( P, varargin )
+
+  if ( nargin < 1 || nargin > 2 )
+    print_usage();
+  endif
+
+  ( ndims(P) == 2 || ndims(P) == 4 ) || \
+      usage("P must be a 2- or 4-dimensional matrix");
+
+  if ( ndims(P) == 2 ) 
+    V = __qnvisitssingle( P, varargin{:} );
+    ch = [1];
+  else
+    [V ch] = __qnvisitsmulti( P, varargin{:} );
+  endif
+endfunction
+%!test
+%! P = [-1 0; 0 0];
+%! fail( "qnvisits(P)", "not a transition probability" );
+%! P = [1 0; 0.5 0];
+%! fail( "qnvisits(P)", "not a stochastic" );
+%! P = [1 0; 0 1]; lambda=[0 -1];
+%! fail( "qnvisits(P,lambda)", "contains negative" );
+
+%!test
+%!
+%! ## Closed, single class network
+%!
+%! P = [0 0.3 0.7; 1 0 0; 1 0 0];
+%! V = qnvisits(P);
+%! assert( V*P,V,1e-5 );
+%! assert( V, [1 0.3 0.7], 1e-5 );
+
+%!test
+%!
+%! ## Open, single class network
+%!
+%! P = [0 0.2 0.5; 1 0 0; 1 0 0];
+%! lambda = [ 0.1 0.3 0.2 ];
+%! V = qnvisits(P,lambda);
+%! assert( V*P+lambda/sum(lambda),V,1e-5 );
+
+%!test
+%!
+%! ## Test tolerance of the qnvisits() function. 
+%! ## This test builds random transition probability matrices and tries
+%! ## to compute the visit counts on them. 
+%!
+%! for k=[5, 10, 20, 50]
+%!   P = rand(k,k);
+%!   nor = sum(P,2);
+%!   P = diag(1./nor)*P;
+%!   V = qnvisits(P);
+%!   assert( V*P, V, 1e-5 );
+%! endfor
+
+%!test
+%!
+%! ## Closed, multiclass network
+%!
+%! C = 2; K = 3; 
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = 1;
+%! P(1,2,1,1) = 1;
+%! P(2,1,2,3) = 1;
+%! P(2,3,2,1) = 1;
+%! V = qnvisits(P);
+%! for c=1:C
+%!   for k=1:K
+%!     assert(V(c,k), sum(sum(V .* P(:,:,c,k))), 1e-5);
+%!   endfor
+%! endfor
+
+%!test
+%!
+%! ## Test multiclass network. Example from Schwetman (figure 7, page 9 of
+%! ## http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1258&context=cstech
+%! ## "Testing network-of-queues software, technical report CSD-TR 330,
+%! ## Purdue University).
+%!
+%! C = 2; K = 4;
+%! P = zeros(C,K,C,K);
+%! # class 1 routing
+%! P(1,1,1,1) = .05;
+%! P(1,1,1,2) = .45;
+%! P(1,1,1,3) = .5;
+%! P(1,2,1,1) = 1;
+%! P(1,3,1,1) = 1;
+%! # class 2 routing
+%! P(2,1,2,1) = .01;
+%! P(2,1,2,3) = .5;
+%! P(2,1,2,4) = .49;
+%! P(2,3,2,1) = 1;
+%! P(2,4,2,1) = 1;
+%! V = qnvisits(P);
+%! for c=1:C
+%!   for i=1:K
+%!     assert(V(c,i), sum(sum(V .* P(:,:,c,i))), 1e-5);
+%!   endfor
+%! endfor
+
+%!test
+%! C = 2; K = 4;
+%! P = zeros(C,K,C,K);
+%! # class 1 routing
+%! P(1,1,1,1) = .05;
+%! P(1,1,1,2) = .45;
+%! P(1,1,1,3) = .5;
+%! P(1,2,1,1) = 0.1;
+%! P(1,3,1,1) = 0.2;
+%! # class 2 routing
+%! P(2,1,2,1) = .01;
+%! P(2,1,2,3) = .5;
+%! P(2,1,2,4) = .49;
+%! P(2,3,2,1) = 0.2;
+%! P(2,4,2,1) = 0.16;
+%! lambda = [0.1 0 0 0.1 ; 0 0 0.2 0.1];
+%! lambda_sum = sum(sum(lambda));
+%! V = qnvisits(P, lambda);
+%! assert( all( all(V>=0) ) );
+%! for i=1:K
+%!   for c=1:C
+%!     assert(V(c,i), lambda(c,i) / lambda_sum + sum(sum(V .* P(:,:,c,i))), 1e-5);
+%!   endfor
+%! endfor
+
+%!test
+%!
+%! ## Network with class switching.
+%! ## This is example in figure 9 of
+%! ## Schwetman, "Implementing the Mean Value Analysis
+%! ## Algorithm fort the solution of Queueing Network Models", Technical
+%! ## Report CSD-TR-355, Department of Computer Science, Purdue Univrsity,
+%! ## Feb 15, 1982
+%! ## http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1285&context=cstech
+%!
+%! C = 2; K = 3;
+%! S = [.01 .07 .10; \
+%!      .05 0.7 .10 ];
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = .7;
+%! P(1,1,1,3) = .2;
+%! P(1,1,2,1) = .1;
+%! P(2,1,2,2) = .3;
+%! P(2,1,2,3) = .5;
+%! P(2,1,1,1) = .2;
+%! P(1,2,1,1) = P(1,3,1,1) = 1;
+%! P(2,2,2,1) = P(2,3,2,1) = 1;
+%! N = [3 0];
+%! V = qnvisits(P);
+%! VV = [10 7 2; 5 1.5 2.5]; # result given in Schwetman; our function computes something different, but that's ok since visit counts are actually ratios
+%! assert( V ./ repmat(V(:,1),1,K), VV ./ repmat(VV(:,1),1,K), 1e-5 );
+
+%!test
+%!
+%! ## two disjoint classes: must produce two disjoing chains
+%!
+%! C = 2; K = 3;
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = 1;
+%! P(1,2,1,1) = 1;
+%! P(2,1,2,3) = 1;
+%! P(2,3,2,1) = 1;
+%! [nc r] = qnvisits(P);
+%! assert( r(1) != r(2) );
+
+%!test
+%!
+%! ## two classes, one chain
+%!
+%! C = 2; K = 3;
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = .5;
+%! P(1,2,2,1) = 1;
+%! P(2,1,2,3) = .5;
+%! P(2,3,1,1) = 1;
+%! [nc r] = qnvisits(P);
+%! assert( r(1) == r(2) );
+
+%!test
+%! 
+%! ## a "Moebius strip". Note that this configuration is invalid, and
+%! ## therefore our algorithm must raise an error. This is because this
+%! ## network has two chains, but both chains contain both classes
+%!
+%! C = 2; K = 2;
+%! P = zeros(C,K,C,K);
+%! P(1,1,2,2) = 1;
+%! P(2,2,1,1) = 1;
+%! P(2,1,1,2) = 1;
+%! P(1,2,2,1) = 1;
+%! fail( "qnvisits(P)", "different");
+
+%!test
+%!
+%! ## Network with two classes representing independent chains.
+%! ## This is example in figure 8 of
+%! ## Schwetman, "Implementing the Mean Value Analysis
+%! ## Algorithm fort the solution of Queueing Network Models", Technical
+%! ## Report CSD-TR-355, Department of Computer Science, Purdue Univrsity,
+%! ## Feb 15, 1982
+%! ## http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1285&context=cstech
+%! 
+%! C = 2; K = 2;
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,3) = P(1,3,1,1) = 1;
+%! P(2,2,2,3) = P(2,3,2,2) = 1;
+%! V = qnvisits(P);
+%! assert( V, [1 0 1; 0 1 1], 1e-5 );
+
+%!demo
+%! P = [ 0 0.4 0.6 0; \
+%!       0.2 0 0.2 0.6; \
+%!       0 0 0 1; \
+%!       0 0 0 0 ];
+%! lambda = [0.1 0 0 0.3];
+%! V = qnvisits(P,lambda);
+%! S = [2 1 2 1.8];
+%! m = [3 1 1 2];
+%! [U R Q X] = qnopensingle( sum(lambda), S, V, m );
+
+##############################################################################
+## Solve the visit equation for multiclass networks with class switching
+## P(r,i,s,j) is the probability that a class-r customer on service
+## center i moves to service center j as a class-s customer. lambda(r,i)
+## is the arrival rate of class-r customers on service center i
+function [V chains] = __qnvisitsmulti( P, lambda )
+  [C, K, C2, K2] = size( P );
+  (K == K2 && C == C2) || \
+      usage( "P must be a [C,K,C,K] matrix");
+
+  chains = [];
+
+  if ( nargin == 1 ) 
+    ## closed network: solve the traffic equations:
+    ## V(s,j) = sum_r sum_i V(r,i) * P(r,i,s,j), for all s,j
+    ## V(s,1) = 1 for all s
+    ##
+    ## FIXME: the constraint V(s,1) = 1 assumes that all customer
+    ## classes visit center 1, which in general could not be the case. 
+    ## A better idea would be to set V(s,i) = 1 for any center i which
+    ## is visited by class s requests. 
+    A = reshape(P,[K*C K*C])-eye(K*C);
+    b = zeros(1,K*C);
+
+    CH = __scc(reshape(P,[C*K C*K])>0);
+    nCH = max(CH); # number of chains
+    CH = reshape(CH,C,K); # chains
+
+    chains = zeros(1,C);
+
+    for k=1:K
+      for c=1:C
+        if ( chains(c) == 0 )
+          chains(c) = CH(c,k);
+        else
+          ( CH(c,k) == 0 || chains(c) == CH(c,k) ) || \
+              error("Class %d belongs to different chains",c);
+        endif
+      endfor
+    endfor
+
+    ## Since there may be queueing centers which are never
+    ## visited by some chain(s), we must be careful here. Consider the
+    ## following example:
+
+    ##  +---------------------------+  
+    ##  |  +---+    +---+    +---+  | Class 1
+    ##  +--|   |----|   |----|   |--+
+    ##     | 1 |    | 2 |    | 3 |
+    ##     |   |  ..|   |....|   |..
+    ##     +---+  . +---+    +---+ .  Class 2
+    ##            ..................
+
+    ## There are two classes, 1 and 2. These must correspond to two
+    ## chains; note that server 1 is never visited by class 2. In the
+    ## situation above, CH(2,1) = 0. Obviously, we also have V(2,1) = 0.
+
+    ## To find a solution to the linear system V(s,j) = sum_r sum_i
+    ## V(r,i) P(r,i,s,j) we must set some constraints (otherwise the
+    ## system may be under defined). If node k is never visited by class
+    ## c, we set the constraint V(c,k) = 0; If node k is visited by
+    ## class c as part of chain q, we set constraints(q)=1, and let
+    ## V(c,k) = 1.
+
+    constraints = zeros(1,nCH); # we put one constraint per chain 
+
+    for c=1:C
+      for k=1:K
+        cc = CH(c,k);
+        if ( cc == 0 || constraints(cc) == 0 )
+	  ii = sub2ind([C K],c,k);
+	  A(:,ii) = 0;
+	  A(ii,ii) = 1;
+	  if ( cc > 0 )
+            ## we put one constraint for this chain
+	    constraints(cc) = 1;
+	    b(ii) = 1;
+          else
+            b(ii) = 0;
+	  endif
+        endif
+      endfor
+    endfor
+    V = reshape(b/A, C, K);
+  else
+    ## open network: solve the traffic equations: V(s,j) = lambda(s,j) /
+    ## lambda + sum_r sum_i V(r,i) * P(r,i,s,j), for all s,j where
+    ## lambda is defined as sum_r sum_i lambda(r,i)
+  
+    [C,K] == size(lambda) || \
+        usage( "lambda size mismatch" );
+    
+    ## solve the traffic equation
+    A = eye(K*C) - reshape(P,[K*C K*C]);
+    b = reshape(lambda / sum(sum(lambda)), [1,K*C]);
+    V = reshape(b/A, [C, K]);
+  endif
+  ## Make sure that no negative values appear (sometimes, numerical
+  ## errors produce tiny negative values instead of zeros)
+  V = max(0,V);
+endfunction
+
+## compute strongly connected components using Kosaraju's algorithm.
+## This is not as efficient as Tarjan's algorithm, as it requires two
+## graph visits.
+##
+## In this implementation, an isolated node without self loops will NOT
+## belong to any SCC. Although this is not formally correct from the
+## graph theoretic point of view, it is necessary to compute chains
+## correctly.
+function s = __scc(G)
+  assert(issquare(G));
+  N = rows(G);
+  GF = (G>0);
+  GB = (G'>0);
+  s = zeros(N,1);
+  c=1;
+  for n=1:N
+    if (s(n) == 0)
+      fw = __dfs(GF,n);
+      bw = __dfs(GB,n);
+      r = (fw & bw);
+      s(r) = c++;
+    endif
+  endfor
+endfunction
+
+## Executes a dfs visit on graph G, starting from source node s
+function v = __dfs(G, s)
+  assert( issquare(G) );
+  N = rows(G);
+  v = stack = zeros(1,N); ## v(i) == 1 iff node i has been visited
+  q = 1; # first empty slot in queue
+  stack(q++) = s;
+  ## Note: node s is NOT marked as visited; it will me marked as visited
+  ## only if we visit it again. This is necessary to ensure that
+  ## isolated nodes without self loops will not belong to any SCC.
+  while( q>1 )
+    n = stack(--q);
+    ## explore neighbors of n: all f in G(n,:) such that v(f) == 0
+    
+    ## The following instruction is equivalent to:
+    ##    for f=find(G(n,:))
+    ##      if ( v(f) == 0 )
+    for f = find ( G(n,:) & (v==0) )
+      stack(q++) = f;
+      v(f) = 1;
+    endfor
+  endwhile
+endfunction
+
+##############################################################################
+## Solve the visit equation for single class networks.
+function V = __qnvisitssingle( P, lambda )
+
+  issquare(P)  || \
+      usage( "P must be a square matrix" );
+
+  N = size(P,1);
+  V = zeros(N,N);
+
+  all( all(P>=0) ) && all( sum(P,2)<=1+1e-5 ) || \
+      usage( "P is not a transition probability matrix" );
+
+  if ( nargin < 2 )
+    ##
+    ## Closed network
+    ##
+    all( abs( sum(P,2)-1 ) < 1e-5 ) || \
+        error( "P is not a stochastic matrix for closed networks" );
+
+    A = P-eye(N);
+    b = zeros(1,N);
+    i = 1; # reference station
+    A(:,i) = 0; A(i,i) = 1;
+    b(i) = 1;
+    V = b/A;
+  else
+    ## Open network
+    ( isvector(lambda) && length(lambda) == N ) || \
+        usage( "lambda size mismatch" );
+    all( lambda>= 0 ) || \
+        usage( "lambda contains negative values" );
+
+    A = eye(N)-P;
+    b = lambda / sum(lambda);
+    V = b/A;
+  endif
+  ## Make sure that no negative values appear (sometimes, numerical
+  ## errors produce tiny negative values instead of zeros)
+  V = max(0,V);
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/scripts/Makefile	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,31 @@
+DISTFILES=gethelp.cc munge-texi.cc Makefile mkdoc pmva.y pmva.l test.pmva
+
+.PHONY: clean
+
+ALL: gethelp munge-texi
+
+gethelp: gethelp.cc
+
+munge-texi: munge-texi.cc
+
+dist:
+	ln $(DISTFILES) ../`cat ../fname`/scripts/
+
+clean:
+	\rm -f *~ *.o y.tab.* lex.yy.*
+
+distclean: clean
+	\rm -f gethelp munge-texi
+
+pmva: y.tab.o lex.yy.o
+	$(CC) y.tab.o lex.yy.o -o $@
+
+lex.yy.o: lex.yy.c y.tab.h
+
+y.tab.o: y.tab.c y.tab.h pmva.h
+
+y.tab.c y.tab.h: pmva.y
+	bison -y -l -d $<
+
+lex.yy.c: pmva.l
+	flex -L $<
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/scripts/gethelp.cc	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,162 @@
+/*
+
+Copyright (C) 1999, 2000, 2003, 2007 John W. Eaton
+Copyright (C) 2011 Moreno Marzolla
+
+This file is part of the queueing toolbox.
+
+The queueing toolbox 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 3 of the
+License, or (at your option) any later version.
+
+The queueing toolbox 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.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (__DECCXX)
+#define __USE_STD_IOSTREAM
+#endif
+
+#include <string>
+#include <iostream>
+#include <cstdio>
+
+#ifndef NPOS
+#define NPOS std::string::npos
+#endif
+
+static bool
+looks_like_octave_copyright (const std::string& s)
+{
+  // Perhaps someday we will want to do more here, so leave this as a
+  // separate function.
+
+  return (s.substr (0, 9) == "Copyright");
+}
+
+// Eat whitespace and comments from FFILE, returning the text of the
+// first block of comments that doesn't look like a copyright notice,
+
+static std::string
+extract_help_text (void)
+{
+  std::string help_txt;
+
+  bool first_comments_seen = false;
+  bool begin_comment = false;
+  bool have_help_text = false;
+  bool in_comment = false;
+  bool discard_space = true;
+  int c;
+
+  while ((c = std::cin.get ()) != EOF)
+    {
+      if (begin_comment)
+	{
+	  if (c == '%' || c == '#')
+	    continue;
+	  else if (discard_space && c == ' ')
+	    {
+	      discard_space = false;
+	      continue;
+	    }
+	  else
+	    begin_comment = false;
+	}
+
+      if (in_comment)
+	{
+	  if (! have_help_text)
+	    {
+	      first_comments_seen = true;
+	      help_txt += (char) c;
+	    }
+
+	  if (c == '\n')
+	    {
+	      in_comment = false;
+	      discard_space = true;
+
+	      if ((c = std::cin.get ()) != EOF)
+		{
+		  if (c == '\n')
+		    break;
+		}
+	      else
+		break;
+	    }
+	}
+      else
+	{
+	  switch (c)
+	    {
+	    case ' ':
+	    case '\t':
+	      if (first_comments_seen)
+		have_help_text = true;
+	      break;
+
+	    case '\n':
+	      if (first_comments_seen)
+		have_help_text = true;
+	      continue;
+
+	    case '%':
+	    case '#':
+	      begin_comment = true;
+	      in_comment = true;
+	      break;
+
+	    default:
+	      goto done;
+	    }
+	}
+    }
+
+ done:
+
+  if (! help_txt.empty ())
+    {
+      if (looks_like_octave_copyright (help_txt)) 
+	help_txt.resize (0);
+
+      if (help_txt.empty ())
+	help_txt = extract_help_text ();
+    }
+
+  return help_txt;
+}
+
+int
+main (int argc, char **argv)
+{
+  std::string name;
+
+  if (argc != 2)
+    {
+      std::cerr << "usage: gethelp name\n";
+      return 1;
+    }
+  else
+    name = argv[1];
+
+  std::string help_text = extract_help_text ();  
+
+  if (! help_text.empty ())
+    {
+      std::cout << "" << name << "\n" << help_text;
+
+      if (help_text[help_text.length () - 1] != '\n')
+	std::cout << "\n";
+    }
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/scripts/mkdoc	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,45 @@
+#! /bin/sh
+#
+# Copyright (C) 1999, 2002, 2005, 2007 John W. Eaton
+#
+# This file is part of Octave.
+# 
+# Octave 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 3 of the License, or (at
+# your option) any later version.
+# 
+# Octave 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.
+# 
+# You should have received a copy of the GNU General Public License
+# along with Octave; see the file COPYING.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+# Adapted to the queueing toolbox by Moreno Marzolla <marzolla (at) cs.unibo.it>, august 2008.
+
+set -e
+
+if test $# != 1; then
+  d=.
+else
+  d=$1
+fi
+
+if test -f gethelp; then
+  cat << EOF
+### DO NOT EDIT!
+###
+### This file is generated automatically from the qnetwork sources.
+### Edit those files instead and run make to update this file.
+
+EOF
+  find $d -name '*.m' | \
+    sed "s,\(.*\)/\(.*\)\.m,./gethelp \2 < & | sed 's/^ *@/@/'," | \
+    /bin/sh
+else
+  echo "gethelp program seems to be missing!" 1>&2
+  exit 1
+fi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/scripts/munge-texi.cc	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,244 @@
+/*
+
+Copyright (C) 1999, 2000, 2002, 2003, 2005, 2007 John W. Eaton
+
+This file is part of Octave.
+
+Octave 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 3 of the License, or (at your
+option) any later version.
+
+Octave 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.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (__DECCXX)
+#define __USE_STD_IOSTREAM
+#endif
+
+#include <cctype>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <map>
+
+#include <cstdlib>
+#include <cstring>
+
+static const char doc_delim = '';
+
+static std::map<std::string, std::string> help_text;
+
+static void
+fatal (const std::string& msg)
+{
+  std::cerr << msg << "\n";
+  exit (1);
+}
+
+static void
+usage (void)
+{
+  std::cerr << "usage: munge-texi -d DOCSTRING-FILE file ...\n";
+  exit (1);
+}
+
+static std::string
+extract_symbol_name (std::istream& is)
+{
+  std::string symbol_name;
+
+  int c;
+  while ((c = is.get ()) != EOF && c != '\n')
+    symbol_name += (char) c;
+
+  return symbol_name;
+}
+
+static std::string
+extract_docstring (std::istream& is)
+{
+  std::string doc;
+
+  int c;
+  while ((c = is.get ()) != EOF && c != doc_delim)
+    doc += (char) c;
+
+  return doc;
+}
+
+static void
+skip_comments (std::ifstream& is)
+{
+  int c;
+
+  bool in_comment = false;
+
+  while ((c = is.get ()) != EOF)
+    {
+      if (c == '#')
+	in_comment = true;
+      else if (c == '\n')
+	in_comment = false;
+      else if (! (in_comment || ::isspace (c)))
+	{
+	  is.putback (c);
+	  break;
+	}
+    }
+}
+
+static void
+process_doc_file (const std::string& fname)
+{
+  std::ifstream infile (fname.c_str ());
+
+  if (infile)
+    {
+      skip_comments (infile);
+
+      if (infile.get () != doc_delim)
+	fatal ("invalid doc file format");
+
+      std::string symbol_name;
+
+      do
+	{
+	  symbol_name = extract_symbol_name (infile);
+
+	  if (! symbol_name.empty ())
+	    {
+	      std::string doc_string = extract_docstring (infile);
+
+	      if (help_text.find (symbol_name) != help_text.end ())
+		std::cerr << "ignoring duplicate entry for "
+			  << symbol_name << "\n";
+	      else
+		help_text[symbol_name] = doc_string;
+	    }
+	}
+      while (! symbol_name.empty ());
+    }
+  else
+    fatal ("unable to open docfile");
+}
+
+static void
+process_texi_input_file (std::istream& is, std::ostream& os)
+{
+  os << "@c DO NOT EDIT!  Generated automatically by munge-texi.\n\n";
+
+  bool bol = true;
+
+  int c;
+  while ((c = is.get ()) != EOF)
+    {
+      if (bol)
+	{
+	  if (c == '@')
+	    {
+	      std::string symbol_name;
+
+	      char buf[16];
+	      int i = 0;
+	      buf[i++] = c;
+
+	      if ((buf[i++] = (char) is.get ()) == 'D'
+		  && (buf[i++] = (char) is.get ()) == 'O'
+		  && (buf[i++] = (char) is.get ()) == 'C'
+		  && (buf[i++] = (char) is.get ()) == 'S'
+		  && (buf[i++] = (char) is.get ()) == 'T'
+		  && (buf[i++] = (char) is.get ()) == 'R'
+		  && (buf[i++] = (char) is.get ()) == 'I'
+		  && (buf[i++] = (char) is.get ()) == 'N'
+		  && (buf[i++] = (char) is.get ()) == 'G'
+		  && (buf[i++] = (char) is.get ()) == '(')
+		{
+		  while ((c = is.get ()) != EOF && c != ')')
+		    symbol_name += (char) c;
+
+		  if (is.eof ())
+		    fatal ("end of file while reading @DOCSTRING command");
+		  else
+		    {
+		      std::string doc_string = help_text[symbol_name];
+
+		      int j = 0;
+		      while (doc_string[j] == ' ')
+			j++;
+
+		      if (doc_string.substr (j, 15) == "-*- texinfo -*-")
+			{
+			  j += 15;
+
+			  while (isspace (doc_string[j]))
+			    j++;
+
+			  // Make `see also' references in functions
+			  // possible using @anchor{TAG} (new with
+			  // Texinfo 4.0).
+
+			  os << "@anchor{doc-" << symbol_name << "}\n";
+
+			  os << doc_string.substr (j);
+			}
+		      else
+			os << doc_string;
+		    }
+		}
+	      else
+		{
+		  buf[i] = '\0';
+		  os << buf;
+
+		  if (buf[i - 1] == '\n')
+		    bol = true;
+		}
+	    }
+	  else
+	    os.put ((char) c);
+	}
+      else
+	{
+	  if (c == '\n')
+	    bol = true;
+
+	  os.put ((char) (c));
+	}
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  while (*++argv)
+    {
+      if (! strcmp (*argv, "-d"))
+	{
+	  if (*++argv)
+	    process_doc_file (*argv);
+	  else
+	    usage ();
+	}
+      else
+	break;
+    }
+
+  process_texi_input_file (std::cin, std::cout);
+
+  return 0;
+}
+
+/*
+;;; Local Variables: ***
+;;; mode: C++ ***
+;;; End: ***
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/scripts/pmva.l	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,78 @@
+/****************************************************************************
+ *
+ * pmva.l
+ *
+ * Lexer for the PMVA notation.
+ *
+ * Copyright (C) 2011 Moreno Marzolla
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+/*
+ * See pmva.y for details
+ */
+%{
+#include "stdio.h"
+#include "pmva.h"
+#include "y.tab.h"
+int linenum=1;
+%}
+
+DIGITS [0-9]+
+ID     [a-zA-Z][A-Za-z0-9_]*
+WS     [ \t]+
+LF     [\r\n]
+FLOAT  {DIGITS}?\.{DIGITS}
+INT    {DIGITS}
+
+%%
+
+{WS}     /* nothing to do */
+{LF}     { linenum++; }
+classes  { return TOK_CLASSES; }
+servers  { return TOK_SERVERS; }
+routing  { return TOK_ROUTING; }
+network  { return TOK_NETWORK; }
+end      { return TOK_END; }
+FCFS     { return TOK_FCFS; }
+PS       { return TOK_PS; }
+IS       { return TOK_IS; }
+LI       { return TOK_LI; }
+MS       { return TOK_MS; }
+LD       { return TOK_LD; }
+POP      { return TOK_POP; }
+MVA	 { return TOK_MVA; }
+ASYMP    { return TOK_ASYMP; }
+"=>"     { return TOK_ARROW; }
+{FLOAT}  { return TOK_FLOAT; }
+{INT}    { yylval.ival = atoi(yytext); return TOK_INT; }
+{ID}     { 
+		if ((yylval.id=malloc(yyleng+1)) == NULL) {
+                     fprintf(stderr, "malloc() failed for id*\n");
+                     exit(1);
+                }
+                strncpy(yylval.id, yytext, yyleng);
+                yylval.id[yyleng] = '\0';
+	        return TOK_ID; 
+         }
+.        { return yytext[0]; }
+
+%%
+int yywrap( void )
+{
+return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/scripts/pmva.y	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,271 @@
+/****************************************************************************
+ *
+ * pmva.y
+ *
+ * This grammar recognizes the PMVA notation for describing
+ * queueing network models. This file is part of the queueing toolbox.
+ *
+ * Copyright (C) 2011, 2012 Moreno Marzolla
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+/*
+ * PMVA (Purdue Mean Value Analysis program) is described in
+ * Jeff Brumfield, "PMVA - Purdue Mean Value Analysis Program User's Guide"
+ * Technical Report CSD-TR-383, april 1981, Purdue University:
+ *
+ * http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1309&context=cstech
+ *
+ * This file contains a grammar for PMVA, according to the specification
+ * above. At the moment, this grammar is only used to implement a parser
+ * which does absolutely nothing (apart recognizing PMVA models). 
+ * Eventually, this grammar will evolve in a suitable software tool
+ * for generating an Octave model suitable for analysis with 
+ * the queueing toolbox.
+ */
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pmva.h"
+
+extern int linenum;
+extern FILE *yyin, *yyout;
+
+#define YYERROR_VERBOSE 1
+%}
+
+%union{
+  int ival;
+  float fval;
+  char* id;
+};
+
+%token <id> TOK_ID
+%token <ival> TOK_INT
+%token <fval> TOK_FLOAT
+%token TOK_CLASSES
+%token TOK_SERVERS
+%token TOK_ROUTING
+%token TOK_FCFS
+%token TOK_PS
+%token TOK_IS
+%token TOK_LCFS
+%token TOK_LI
+%token TOK_MS
+%token TOK_LD
+%token TOK_ARROW
+%token TOK_NETWORK
+%token TOK_END
+%token TOK_MVA
+%token TOK_ASYMP
+%token TOK_POP
+
+%left ','
+%left TOK_ARROW
+
+%% /* The grammar follows */
+
+input: /* empty */
+| TOK_NETWORK ';' blocks TOK_END ';' solution_block
+;
+
+blocks: /* empty */
+| blocks block
+;
+
+block: TOK_CLASSES class_list ';'
+| TOK_SERVERS server_list ';'
+| TOK_ROUTING routing_node_list ';'
+;
+
+class_list: /* empty */
+| TOK_ID		{ push_class($1); }
+| class_list ',' TOK_ID { push_class($3); }
+;
+
+server_list: /* empty */
+| server
+| server_list ',' server
+;
+
+server: TOK_ID { push_server($1); } queueing_discipline server_type service_times
+;
+
+queueing_discipline: /* empty (assume FCFS) */
+| TOK_FCFS { server_list->queueing_discipline = FCFS; }
+| TOK_PS   { server_list->queueing_discipline = PS; }
+| TOK_IS   { server_list->queueing_discipline = IS; printf("m(%d) = -1;\n", n_servers); }
+| TOK_LCFS { server_list->queueing_discipline = LCFS; }
+;
+
+server_type:  { printf("m(%d) = 1;\n", n_servers); } /* if not specified, assume LI */
+| TOK_LI	{ server_list->server_type = LI; printf("m(%d) = 1;\n", n_servers); }
+| TOK_MS TOK_INT { server_list->server_type = MS; server_list->num_servers = $2; printf("m(%d) = %d;\n", n_servers, $2); }
+| TOK_LD	{ server_list->server_type = LD; printf("m(%d) = 1;\n", n_servers); }
+;
+
+service_times: service_time_spec
+| service_times service_time_spec
+;
+
+service_time_spec: class_name visit_ratio service_time
+;
+
+class_name: /* empty */ { if (n_classes > 1) { printf("Class spwcified for server %s must be given for multiclass networks\n", server_list->name); exit(-1); } }
+| TOK_ID '='
+;
+
+visit_ratio: /* empty */
+| '[' TOK_FLOAT ']'
+;
+
+service_time: TOK_FLOAT
+| '(' serv_time_list ')'
+;
+
+serv_time_list: TOK_FLOAT
+| serv_time_list ',' TOK_FLOAT
+;
+
+routing_node_list: /* empty */
+| nonempty_routing_spec
+| routing_node_list ',' nonempty_routing_spec
+;
+
+nonempty_routing_spec: node_list
+| nonempty_routing_spec TOK_ARROW node_list
+;
+
+node_list: TOK_ID opt_class_name opt_routing_prob
+| node_list TOK_ID opt_class_name opt_routing_prob
+;
+
+opt_class_name: /* empty */
+| '/' TOK_ID
+;
+
+opt_routing_prob: /* empty */
+| '(' TOK_FLOAT ')'
+;
+
+solution_block: /* empty */
+| TOK_ASYMP ';'
+| TOK_MVA pop_description ';'
+;
+
+pop_description: TOK_POP '=' TOK_INT
+| TOK_POP '=' '(' pop_list ')' 
+;
+
+pop_list: TOK_INT
+| pop_list ',' TOK_INT
+;
+
+%%
+
+server_node_t* server_list = 0;
+class_node_t* class_list = 0;
+
+int n_servers = 0;
+int n_classes = 0;
+
+/* 
+ * Push a new server in front of the server list. Return the new head
+ * of the list (the global variable server_list is also updated to
+ * point to the newly created object). The caller transfers ownership
+ * of the pointer name.
+ */
+server_node_t* push_server( const char* name )
+{
+  server_node_t* server = malloc( sizeof(server_node_t) );
+  server->id = ++n_servers;
+  server->name = name;
+  server->queueing_discipline = FCFS;
+  server->server_type = LI;
+  server->num_servers = 1;
+  server->next = server_list;
+  server_list = server;
+  printf("servers{%d} = \"%s\";\n", n_servers, name );
+  return server;
+}
+
+class_node_t* push_class( const char* name )
+{
+  class_node_t* class = malloc( sizeof(class_node_t) );
+  class->id = ++n_classes;
+  class->name = name;
+  class->next = class_list;
+  class_list = class;
+  printf("classes{%d}=\"%s\";\n", n_classes, name);
+  return class;
+}
+
+void dump_server_list( void )
+{
+  server_node_t* server = server_list;
+  while ( server ) {
+    printf("[%d] %s ",server->id, server->name);
+
+    switch ( server->server_type ) {
+    case LI: printf("LI "); break;
+    case LD: printf("LD "); break;
+    case MS: printf("MS %d ", server->num_servers); break;
+    default: printf("Unrecognized server type %d\n", server->server_type);
+      exit(-1);
+    }
+
+    switch ( server->queueing_discipline ) {
+    case FCFS: printf("FCFS "); break;
+    case PS: printf("PS "); break;
+    case LCFS: printf("LCFS "); break;
+    case IS: printf("IS "); break;
+    default: printf("Unrecognized queueing discipline %d\n", server->queueing_discipline );
+      exit(-1);
+    }      
+    printf("\n");
+    server = server->next;
+  }
+}
+
+void dump_class_list( void )
+{
+  class_node_t* class = class_list;
+  while( class ) {
+    printf("[%d] %s\n", class->id, class->name );
+    class = class->next;
+  }
+}
+
+void yyerror(char const *s)
+{
+  fprintf(stderr, "line %d, %s\n", linenum, s);
+  /*fprintf(stderr, "%s: (Error) line %d: %s\n", nomefile, linea, s); */
+}
+
+int main( int argc, char **argv )
+{
+  int res;
+  ++argv, --argc;  /* skip over program name */
+  if ( argc > 0 )
+    yyin = fopen( argv[0], "r" );
+  else
+    yyin = stdin;
+  
+  res = yyparse();
+  /* dump_server_list();
+     dump_class_list(); */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/scripts/test.pmva	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,15 @@
+network;
+classes A, B, C;
+classes D, E, F;
+servers terminals IS LI 1.0;
+servers cpu2 PS MS 2 BATCH=20.0;
+servers
+	terminals IS LI 4.0,
+	cpu PS LI 0.025,
+	disk FCFS LI 0.050,
+	tape FCFS LI 0.150,
+	cpu2 PS MS 2 BATCH=0.064 INTERACTIVE=(0.4 , 0.4, 0.3),
+	disk2 FCFS LD ALL=(0.0250, 0.0225, 0.0214, 0.0208);
+routing
+	terminals/FOO => cpu / BAR (0.9) disk / BAZ (0.1) => tape / ID;
+end;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/test/Makefile	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,16 @@
+DISTFILES=Makefile fntests.m
+
+.PHONY: check dist clean
+
+ALL:
+
+check:
+	cd ../inst && octave -q ../test/fntests.m
+
+dist:
+	ln $(DISTFILES) ../`cat ../fname`/test/
+
+clean:
+	\rm -f *~ fntests.log
+
+distclean: clean
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/queueing/test/fntests.m	Fri Feb 03 12:07:15 2012 +0000
@@ -0,0 +1,246 @@
+## Copyright (C) 2005, 2006, 2007 David Bateman
+## Modifications Copyright (C) 2009, 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox. It is based on the fntests.m
+## script included in GNU Octave.
+##
+## Octave 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 3 of the License, or (at
+## your option) any later version.
+##
+## Octave 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+clear all;
+
+global files_with_no_tests = {};
+global files_with_tests = {};
+global topsrcdir;
+global topbuilddir;
+global skip_files;
+
+currdir = canonicalize_file_name (".");
+
+if (nargin == 1)
+  xdir = argv(){1};
+else
+  xdir = ".";
+endif
+
+srcdir = canonicalize_file_name (xdir);
+topsrcdir = canonicalize_file_name (fullfile (xdir, ".."));
+topbuilddir = canonicalize_file_name (fullfile (currdir, ".."));
+
+if (strcmp (currdir, srcdir))
+  testdirs = {srcdir};
+else
+  testdirs = {currdir, srcdir};
+endif
+
+#src_tree = canonicalize_file_name (fullfile (topsrcdir, "src"));
+#liboctave_tree = canonicalize_file_name (fullfile (topsrcdir, "liboctave"));
+script_tree = canonicalize_file_name (fullfile (topsrcdir, "inst"));
+#local_script_tree = canonicalize_file_name (fullfile (currdir, "../scripts"));
+
+fundirs = {script_tree};
+skip_files = { "lee_et_al_98.m" };
+
+# if (! strcmp (currdir, srcdir))
+#   fundirs{end+1} = local_script_tree;
+# endif
+
+function print_test_file_name (nm)
+  filler = repmat (".", 1, 55-length (nm));
+  printf ("  %s %s", nm, filler);
+endfunction
+
+function print_pass_fail (n, p)
+  if (n > 0)
+    printf (" PASS %4d/%-4d", p, n);
+    nfail = n - p;
+    if (nfail > 0)
+      printf (" FAIL %d", nfail);
+    endif
+  endif
+  printf ("\n");
+endfunction
+
+function y = hastests (f)
+  fid = fopen (f);
+  str = fscanf (fid, "%s");
+  fclose (fid);
+  y = (findstr (str, "%!test") || findstr (str, "%!assert")
+       || findstr (str, "%!error") || findstr (str, "%!warning"));
+endfunction
+
+function r = skip(nm)
+  global skip_files;
+  r = 0;
+  for sk=skip_files
+    if ( strcmp(nm,skip_files) )
+      r = 1;
+      break;
+    endif
+  endfor
+endfunction
+
+function [dp, dn, dxf, dsk] = run_test_dir (fid, d);
+  global files_with_tests;
+  global files_with_no_tests;
+  lst = dir (d);
+  dp = dn = dxf = dsk = 0;
+  for i = 1:length (lst)
+    nm = lst(i).name;
+    if (length (nm) > 5 && strcmp (nm(1:5), "test_")
+	&& strcmp (nm((end-1):end), ".m"))
+      p = n = 0;
+      ffnm = fullfile (d, nm);
+      if (hastests (ffnm))
+	print_test_file_name (nm);
+	[p, n, xf, sk] = test (nm(1:(end-2)), "quiet", fid);
+	print_pass_fail (n, p);
+	files_with_tests(end+1) = ffnm;
+      else
+	files_with_no_tests(end+1) = ffnm;
+      endif
+      dp += p;
+      dn += n;
+      dxf += xf;
+      dsk += sk;
+    endif
+  endfor
+endfunction
+
+function [dp, dn, dxf, dsk] = run_test_script (fid, d);
+  global files_with_tests;
+  global files_with_no_tests;
+  global topsrcdir;
+  global topbuilddir;
+  lst = dir (d);
+  dp = dn = dxf = dsk = 0;
+  for i = 1:length (lst)
+    nm = lst(i).name;
+    if (lst(i).isdir && ! strcmp (nm, ".") && ! strcmp (nm, "..")
+	&& ! strcmp (nm, "CVS"))
+      [p, n, xf, sk] = run_test_script (fid, [d, "/", nm]);
+      dp += p;
+      dn += n;
+      dxf += xf;
+      dsk += sk;
+    endif
+  endfor
+  for i = 1:length (lst)
+    nm = lst(i).name;
+    if (((length (nm) > 3 && strcmp (nm((end-2):end), ".cc"))
+	 || (length (nm) > 2 && strcmp (nm((end-1):end), ".m")))
+        && !skip(nm))
+      f = fullfile (d, nm);
+      p = n = xf = 0;
+      ## Only run if it contains %!test, %!assert %!error or %!warning
+      if (hastests (f))
+	tmp = strrep (f, [topsrcdir, "/"], "");
+	tmp = strrep (tmp, [topbuilddir, "/"], "../");
+	print_test_file_name (tmp);
+	[p, n, xf, sk] = test (f, "quiet", fid);
+	print_pass_fail (n, p);
+	dp += p;
+	dn += n;
+	dxf += xf;
+	dsk += sk;
+	files_with_tests(end+1) = f;
+      else
+	files_with_no_tests(end+1) = f;
+      endif
+    endif
+  endfor 
+  ##  printf("%s%s -> passes %d of %d tests\n", ident, d, dp, dn);
+endfunction
+
+function printf_assert (varargin)
+  global _assert_printf;
+  _assert_printf = cat (2, _assert_printf, sprintf (varargin{:}));
+endfunction
+
+function ret = prog_output_assert (str)
+  global _assert_printf;
+  if (isempty (_assert_printf))
+    ret = isempty (str);
+  elseif (_assert_printf(end) == "\n")
+    ret = strcmp (_assert_printf(1:(end-1)), str);
+  else
+    ret = strcmp (_assert_printf, str);
+  endif
+  _assert_printf = "";
+endfunction
+
+pso = page_screen_output ();
+warn_state = warning ("query", "quiet");
+warning ("on", "quiet");
+try
+  page_screen_output (0);
+  fid = fopen ("fntests.log", "wt");
+  if (fid < 0)
+    error ("could not open fntests.log for writing");
+  endif
+  test ("", "explain", fid);
+  dp = dn = dxf = dsk = 0;
+  printf ("\nIntegrated test scripts:\n\n");
+  for i = 1:length (fundirs)
+    [p, n, xf, sk] = run_test_script (fid, fundirs{i});
+    dp += p;
+    dn += n;
+    dxf += xf;
+    dsk += sk;
+  endfor
+  printf ("\nFixed test scripts:\n\n");
+  for i = 1:length (testdirs)
+    [p, n, xf, sk] = run_test_dir (fid, testdirs{i});
+    dp += p;
+    dn += n;
+    dxf += xf;
+    dsk += sk;
+  endfor
+  printf ("\nSummary:\n\n  PASS %6d\n", dp);
+  nfail = dn - dp;
+  printf ("  FAIL %6d\n", nfail);
+  if (dxf > 0)
+    if (dxf > 1)
+      t1 = "were";
+      t2 = "failures";
+    else
+      t1 = "was";
+      t2 = "failure";
+    endif
+    printf ("\nThere %s %d expected %s (see fntests.log for details).\n",
+	    t1, dxf, t2);
+    printf ("\nExpected failures are known bugs. Please help improve\n");
+    printf ("the queueing toolbox by contributing fixes for them.\n");
+  endif
+  if (dsk > 0)
+    printf ("\nThere were %d skipped tests (see fntest.log for details).\n", dsk);
+    printf ("Skipped tests are features that are disabled in this version\n");
+    printf ("of the queueing toolbox\n");
+  endif
+
+  n_files_with_no_tests = length (files_with_no_tests);
+  n_files = n_files_with_no_tests + length (files_with_tests);
+  printf ("\n%d (of %d) files have no tests.  Please help improve the queueing toolbox by\n",
+	  n_files_with_no_tests, n_files);
+  printf ("contributing tests for these files (see the list in the file fntests.log).\n");
+  fprintf (fid, "\nFiles with no tests:\n\n%s",
+	  list_in_columns (files_with_no_tests, 80));
+  fclose (fid);
+  page_screen_output (pso);
+  warning (warn_state.state, "quiet");
+catch
+  page_screen_output (pso);
+  warning (warn_state.state, "quiet");
+  disp (lasterr ());
+end_try_catch