[Pkg-octave-commit] [octave-queueing.git] 01/01: Imported Upstream version 1.2.3

Rafael Laboissière rlaboiss-guest at moszumanska.debian.org
Tue Aug 30 13:13:22 UTC 2016


This is an automated email from the git hooks/post-receive script.

rlaboiss-guest pushed a commit to branch master
in repository octave-queueing.git.

commit 65622a4be7676fd24ae4419f5498af824ebb44bc
Author: Rafael Laboissiere <rafael at debian.org>
Date:   Sat Aug 20 16:19:33 2016 -0300

    Imported Upstream version 1.2.3
---
 COPYING                        |  674 ++++
 DESCRIPTION                    |   21 +
 NEWS                           |   87 +
 doc/INSTALL                    |  119 +
 doc/README                     |   28 +
 doc/gpl.texi                   |  721 ++++
 doc/installation.texi          |  566 ++++
 doc/markovchains.texi          | 1427 ++++++++
 doc/power.eps                  |  809 +++++
 doc/qn_closed_multi.eps        |  209 ++
 doc/qn_closed_multi.fig        |   39 +
 doc/qn_closed_multi_apl.eps    |  197 ++
 doc/qn_closed_multi_apl.fig    |   33 +
 doc/qn_closed_multi_cs.eps     |  316 ++
 doc/qn_closed_multi_cs.fig     |   71 +
 doc/qn_closed_single.eps       |  216 ++
 doc/qn_closed_single.fig       |   42 +
 doc/qn_open_single.eps         |  240 ++
 doc/qn_open_single.fig         |   50 +
 doc/qn_web_model.eps           |  261 ++
 doc/qn_web_model.fig           |   72 +
 doc/qu_closed_multi_apl.fig    |   41 +
 doc/queueing.html              | 7116 ++++++++++++++++++++++++++++++++++++++++
 doc/queueing.pdf               |  Bin 0 -> 720217 bytes
 doc/queueing.texi              |  221 ++
 doc/queueingnetworks.texi      | 4022 +++++++++++++++++++++++
 doc/references.texi            |  141 +
 doc/singlestation.texi         |  960 ++++++
 doc/summary.texi               |  179 +
 doc/web.eps                    | 1114 +++++++
 inst/ctmc.m                    |  302 ++
 inst/ctmc_bd.m                 |   44 +
 inst/ctmc_check_Q.m            |   71 +
 inst/ctmc_exps.m               |   60 +
 inst/ctmc_fpt.m                |   47 +
 inst/ctmc_mtta.m               |   64 +
 inst/ctmc_taexps.m             |   48 +
 inst/ctmcbd.m                  |   99 +
 inst/ctmcchkQ.m                |   91 +
 inst/ctmcexps.m                |  182 +
 inst/ctmcfpt.m                 |  116 +
 inst/ctmcmtta.m                |  134 +
 inst/ctmctaexps.m              |  153 +
 inst/dtmc.m                    |  189 ++
 inst/dtmc_bd.m                 |   43 +
 inst/dtmc_check_P.m            |   63 +
 inst/dtmc_exps.m               |   52 +
 inst/dtmc_fpt.m                |   96 +
 inst/dtmc_is_irreducible.m     |   97 +
 inst/dtmc_mtta.m               |   82 +
 inst/dtmc_taexps.m             |   46 +
 inst/dtmcbd.m                  |  106 +
 inst/dtmcchkP.m                |   81 +
 inst/dtmcexps.m                |  148 +
 inst/dtmcfpt.m                 |  145 +
 inst/dtmcisir.m                |  133 +
 inst/dtmcmtta.m                |  250 ++
 inst/dtmctaexps.m              |   87 +
 inst/engset.m                  |  123 +
 inst/erlangb.m                 |  115 +
 inst/erlangc.m                 |   97 +
 inst/population_mix.m          |   39 +
 inst/private/expn.m            |   32 +
 inst/private/qncmchkparam.m    |  111 +
 inst/private/qncschkparam.m    |   99 +
 inst/private/qnomchkparam.m    |   95 +
 inst/private/qnoschkparam.m    |   94 +
 inst/private/sumexpn.m         |   58 +
 inst/qnammm.m                  |   45 +
 inst/qnclosed.m                |   95 +
 inst/qnclosedab.m              |   57 +
 inst/qnclosedbsb.m             |   49 +
 inst/qnclosedgb.m              |   43 +
 inst/qnclosedmultimva.m        |   44 +
 inst/qnclosedmultimvaapprox.m  |   66 +
 inst/qnclosedpb.m              |   40 +
 inst/qnclosedsinglemva.m       |   41 +
 inst/qnclosedsinglemvaapprox.m |   64 +
 inst/qnclosedsinglemvald.m     |   40 +
 inst/qncmaba.m                 |  162 +
 inst/qncmbsb.m                 |  153 +
 inst/qncmcb.m                  |  139 +
 inst/qncmmva.m                 |  850 +++++
 inst/qncmmvaap.m               |  256 ++
 inst/qncmnpop.m                |   85 +
 inst/qncmpopmix.m              |  141 +
 inst/qncmva.m                  |   40 +
 inst/qncmvisits.m              |  339 ++
 inst/qnconvolution.m           |   40 +
 inst/qnconvolutionld.m         |   39 +
 inst/qncsaba.m                 |  131 +
 inst/qncsbsb.m                 |  114 +
 inst/qncscmva.m                |  250 ++
 inst/qncsconv.m                |  226 ++
 inst/qncsconvld.m              |  220 ++
 inst/qncsgb.m                  |  250 ++
 inst/qncsmva.m                 |  349 ++
 inst/qncsmvaap.m               |  238 ++
 inst/qncsmvablo.m              |  201 ++
 inst/qncsmvald.m               |  206 ++
 inst/qncspb.m                  |  136 +
 inst/qncsvisits.m              |  133 +
 inst/qnjackson.m               |  116 +
 inst/qnmarkov.m                |  344 ++
 inst/qnmg1.m                   |   39 +
 inst/qnmh1.m                   |   39 +
 inst/qnmix.m                   |  245 ++
 inst/qnmknode.m                |  157 +
 inst/qnmm1.m                   |   39 +
 inst/qnmm1k.m                  |   60 +
 inst/qnmminf.m                 |   42 +
 inst/qnmmm.m                   |   40 +
 inst/qnmmmk.m                  |  115 +
 inst/qnmvablo.m                |   90 +
 inst/qnmvapop.m                |   39 +
 inst/qnom.m                    |  307 ++
 inst/qnomaba.m                 |  112 +
 inst/qnomvisits.m              |  144 +
 inst/qnopen.m                  |   66 +
 inst/qnopenab.m                |   39 +
 inst/qnopenbsb.m               |   50 +
 inst/qnopenmulti.m             |   39 +
 inst/qnopensingle.m            |   40 +
 inst/qnos.m                    |  216 ++
 inst/qnosaba.m                 |  110 +
 inst/qnosbsb.m                 |  109 +
 inst/qnosvisits.m              |  115 +
 inst/qnsolve.m                 |  820 +++++
 inst/qnvisits.m                |  515 +++
 inst/qsammm.m                  |  109 +
 inst/qsmg1.m                   |  101 +
 inst/qsmh1.m                   |  114 +
 inst/qsmm1.m                   |  114 +
 inst/qsmm1k.m                  |  148 +
 inst/qsmminf.m                 |  108 +
 inst/qsmmm.m                   |  153 +
 inst/qsmmmk.m                  |  222 ++
 137 files changed, 34282 insertions(+)

diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -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>.
diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..9c04e17
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,21 @@
+Name: queueing
+Version: 1.2.3
+Date: 2014-03-08
+Author: Moreno Marzolla <moreno.marzolla at unibo.it>
+Maintainer: Moreno Marzolla <moreno.marzolla at unibo.it>
+Title: Octave package for Queueing Networks and Markov chains analysis
+Description: The queueing package provides functions for queueing
+ networks and Markov chains analysis. This package can be used to
+ compute steady-state performance measures for open, closed and mixed
+ networks with single or multiple job classes. Mean Value Analysis
+ (MVA), convolution, and various bounding techniques are
+ implemented. Furthermore, several transient and steady-state
+ performance measures for Markov chains can be computed, such as state
+ occupancy probabilities, mean time to absorption, time-averaged
+ sojourn times and so forth. Discrete- and continuous-time Markov
+ chains are supported.
+Categories: Misc
+Depends: octave (>= 3.2.3)
+Autoload: no
+License: GPLv3+
+Url: http://www.moreno.marzolla.name/software/queueing/
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..ddf3d71
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,87 @@
+Summary of important user-visible changes for queueing-1.2.3
+------------------------------------------------------------------------------
+
+** queueing-1.2.3 contains new features
+
+** Added new functions erlangb (Erlang-B formula), erlangc (Erlang-C formula)
+   and engset
+
+
+Summary of important user-visible changes for queueing-1.2.2
+------------------------------------------------------------------------------
+
+** queueing-1.2.2 is a bug fix release
+
+** Fixed bug in qncmmva: utilization for M/M/m centers was not
+   correctly scaled to [0,1] when m>1
+
+** Added warning to qncsmva and qncmmva to detect and report numerical
+   instability problems
+
+
+Summary of important user-visible changes for queueing-1.2.1
+------------------------------------------------------------------------------
+
+** queueing-1.2.1 contains new features
+
+** Function qnvisits() has been deprecated, and is replaced by
+   qncsvisits(), qnosvisits() (for single-class networks) and
+   qncmvisits(), qnomvisits() (for multiple-class networks). The new
+   functions allow the user to specify the reference station.
+
+
+Summary of important user-visible changes for queueing-1.2.0
+------------------------------------------------------------------------------
+
+** queueing-1.2.0 includes many bug fixes and new features
+
+** The documentation has been restructured, hopefully improving
+   readability and overall organization
+
+** Added functions to compute various kinds of performance bounds on
+   multiclass networks; balanced system, asymptotic and composite
+   bounds are supported.
+
+** Added function qnom for analyzing open, multiclass product-form
+   networks.
+
+** Adopted a new scheme for naming functions; old names are still
+   available for compatibility with existing scripts, but will be
+   removed in future releases.
+
+** Fixed bugs in qncscmva, qncsconv, qncsconvld, qnopensingle,
+   qnopenmulti and qncmmva
+
+
+Summary of important user-visible changes for queueing-1.1.1
+------------------------------------------------------------------------------
+
+** queueing-1.1.1 is a bug fix release
+
+** Increased tolerance in tests for dtmc_fpt and dtmc_mtta to avoid
+   spurious failures on some platforms
+
+** Set "Autoload: no" in the DESCRIPTION file. This means that this
+   package is no longer automatically loaded on Octave startup. To use
+   the queueing package you need to issue the command "pkg load
+   queueing" at the Octave prompt.
+
+
+Summary of important user-visible changes for queueing-1.1.0
+------------------------------------------------------------------------------
+
+** Function ctmc_exps() can now compute the expected sojourn time
+   until absorption for absorbing CTMC
+
+** Functions ctmc_exps() and ctmc_taexps() now accept a scalar as
+   the second argument (time). 
+
+** Function ctmc_bd() now returns the infinitesimal generator matrix Q
+   of the birth-death process with given rates, not the steady-state
+   solution.
+
+** The following new functions have been added: dtmc_bd(), dtmc_mtta(), 
+   ctmc_check_Q(), dtmc_exps(), dtmc_taexps()
+
+** The following deprecated functions have been removed: ctmc_bd_solve(),
+   ctmc_solve(), dtmc_solve()
diff --git a/doc/INSTALL b/doc/INSTALL
new file mode 100644
index 0000000..ea2f422
--- /dev/null
+++ b/doc/INSTALL
@@ -0,0 +1,119 @@
+This file documents the installation procedure of the Octave `queueing'
+package.
+
+   `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' subversion sources.  Do
+     not modify this document directly, as changes will be lost. Modify
+     `doc/installation.txi' instead.
+
+1 Installation and Getting Started
+**********************************
+
+1.1 Installation through Octave package management system
+=========================================================
+
+The most recent version of `queueing' is 1.2.3 and can be downloaded
+from Octave-Forge
+
+   `http://octave.sourceforge.net/queueing/'
+
+   Additional information can be found at
+
+   `http://www.moreno.marzolla.name/software/queueing/'
+
+   To install `queueing', follow these steps:
+
+  1. If you have a recent version of GNU Octave and a network
+     connection, you can install `queueing' directly from Octave
+     command prompt using this command:
+
+          octave:1> pkg install -forge queueing
+
+     The command above will automaticall download and install the latest
+     version of the queueing package from Octave Forge, and install it
+     on your machine.
+
+     If you do not have root access, you can do a local install using:
+
+          octave:1> pkg install -local -forge queueing
+
+     This will install `queueing' within your home directory, and the
+     package will be available to your user only.
+
+  2. Alternatively, you can first download `queueing' from
+     Octave-Forge; then, to install the package in the system-wide
+     location issue this command at the Octave prompt:
+
+          octave:1> pkg install _queueing-1.2.3.tar.gz_
+
+     (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.
+
+     If you do not have root access, you can do a local install using:
+
+          octave:1> pkg install -local queueing-1.2.3.tar.gz
+
+          Note: Octave version 3.2.3 as shipped with Ubuntu 10.04 LTS
+          seems to ignore `-local' and always tries to install the
+          package on the system directory.
+
+  3. Verify that the package is indeed installed using the `pkg list'
+     command at the Octave prompt; after succesfull installation you
+     should see something like that:
+
+          octave:1>pkg list queueing
+          Package Name  | Version | Installation directory
+          --------------+---------+-----------------------
+              queueing  |   1.2.3 | /home/moreno/octave/queueing-1.2.3
+
+  4. Starting from version 1.1.1, `queueing' is no longer automatically
+     loaded on Octave startup. To make the functions available for use,
+     you need to issue the command
+
+          octave:1>pkg load queueing
+
+     at the Octave prompt. To automatically load `queueing' each time
+     Octave starts, you can add the command above to the startup script
+     (usually, `~/.octaverc' on Unix systems).
+
+  5. To completely remove `queueing' from your system, use the `pkg
+     uninstall' command:
+
+          octave:1> pkg uninstall queueing
+
+
+1.2 Manual installation
+=======================
+
+If you want to manually install `queueing' in a custom location, you
+can download the tarball and unpack it somewhere:
+
+     tar xvfz queueing-1.2.3.tar.gz
+     cd queueing-1.2.3/queueing/
+
+   Copy all `.m' files from the `inst/' directory to some target
+location. Then, 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 `/path/to/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.
+
diff --git a/doc/README b/doc/README
new file mode 100644
index 0000000..4968dca
--- /dev/null
+++ b/doc/README
@@ -0,0 +1,28 @@
+===============================================================================
+                        The Octave queueing toolbox
+===============================================================================
+
+Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013
+Moreno Marzolla <moreno.marzolla (at) 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 for single station queueing systems.
+
+The Web page of the queueing toolbox is
+
+http://www.moreno.marzolla.name/software/queueing/
+
+The latest version can be downloaded from Octave forge
+
+http://octave.sourceforge.net/
+
+This package requires GNU Octave; version 3.2.3 or later should work.
+
+The Octave queueing toolbox is distributed under the terms of the GNU
+General Public License, version 3 or later. See the file COPYING for
+details.
+
diff --git a/doc/gpl.texi b/doc/gpl.texi
new file mode 100644
index 0000000..074c6a2
--- /dev/null
+++ b/doc/gpl.texi
@@ -0,0 +1,721 @@
+ at c This file has been automatically generated from gpl.txi
+ at c by proc.m. Do not edit this file, all changes will be lost
+
+ at node Copying
+ at appendix GNU GENERAL PUBLIC LICENSE
+ at cindex warranty
+ at cindex copyright
+
+ at center Version 3, 29 June 2007
+
+ at 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.
+ at end display
+
+ at 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.
+
+ at heading TERMS AND CONDITIONS
+
+ at enumerate 0
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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:
+
+ at enumerate a
+ at item 
+The work must carry prominent notices stating that you modified it,
+and giving a relevant date.
+
+ at 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''.
+
+ at 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.
+
+ at 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.
+ at 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.
+
+ at 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:
+
+ at enumerate a
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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:
+
+ at enumerate a
+ at item
+Disclaiming warranty or limiting liability differently from the terms
+of sections 15 and 16 of this License; or
+
+ at 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
+
+ at 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
+
+ at item
+Limiting the use for publicity purposes of names of licensors or
+authors of the material; or
+
+ at item
+Declining to grant rights under trademark law for use of some trade
+names, trademarks, or service marks; or
+
+ at 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.
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at end enumerate
+
+ at heading END OF TERMS AND CONDITIONS
+
+ at 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.
+
+ at smallexample
+ at 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/}.
+ at 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:
+
+ at smallexample
+ at 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.
+ at 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
+ at 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}.
diff --git a/doc/installation.texi b/doc/installation.texi
new file mode 100644
index 0000000..b9fffd3
--- /dev/null
+++ b/doc/installation.texi
@@ -0,0 +1,566 @@
+ at c This file has been automatically generated from installation.txi
+ at c by proc.m. Do not edit this file, all changes will be lost
+
+ at c -*- texinfo -*-
+
+ at c Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014 Moreno Marzolla
+ at c
+ at c This file is part of the queueing package.
+ at c
+ at c The queueing package is free software; you can redistribute it
+ at c and/or modify it under the terms of the GNU General Public License
+ at c as published by the Free Software Foundation; either version 3 of
+ at c the License, or (at your option) any later version.
+ at c
+ at c The queueing package is distributed in the hope that it will be
+ at c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ at c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ at c GNU General Public License for more details.
+ at c
+ at c You should have received a copy of the GNU General Public License
+ at c along with the queueing package; see the file COPYING.  If not, see
+ at c <http://www.gnu.org/licenses/>.
+
+ at ifset INSTALLONLY
+ at include conf.texi
+
+This file documents the installation procedure of the Octave
+ at code{queueing} package.
+
+ at 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.
+
+ at quotation Note
+This file (@file{INSTALL}) is automatically generated from
+ at file{doc/installation.txi} in the @code{queueing} subversion sources.
+Do not modify this document directly, as changes will be lost. Modify
+ at file{doc/installation.txi} instead.
+ at end quotation
+
+ at end ifset
+
+ at node Installation and Getting Started
+ at chapter Installation and Getting Started
+
+ at menu
+* Installation through Octave package management system::
+* Manual installation::
+* Development sources::
+* Naming Conventions::
+* Quickstart Guide::
+ at end menu
+
+ at c
+ at c
+ at c
+
+ at node Installation through Octave package management system
+ at section Installation through Octave package management system
+
+The most recent version of @code{queueing} is @value{VERSION} and can
+be downloaded from Octave-Forge
+
+ at url{http://octave.sourceforge.net/queueing/}
+
+Additional information can be found at
+
+ at url{http://www.moreno.marzolla.name/software/queueing/}
+
+To install @code{queueing}, follow these steps:
+
+ at enumerate
+
+ at item
+If you have a recent version of GNU Octave and a network connection,
+you can install @code{queueing} directly from Octave command prompt
+using this command:
+
+ at example
+octave:1> @kbd{pkg install -forge queueing}
+ at end example
+
+The command above will automaticall download and install the latest
+version of the queueing package from Octave Forge, and install it on
+your machine. 
+
+If you do not have root access, you can do a local install using:
+
+ at example
+octave:1> @kbd{pkg install -local -forge queueing}
+ at end example
+
+This will install @code{queueing} within your home directory, and the
+package will be available to your user only. 
+
+ at item
+Alternatively, you can first download @code{queueing} from
+Octave-Forge; then, to install the package in the system-wide
+location issue this command at the Octave prompt:
+
+ at example
+octave:1> @kbd{pkg install @emph{queueing- at value{VERSION}.tar.gz}}
+ at end example
+
+ at noindent (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.
+
+If you do not have root access, you can do a local install using:
+
+ at example
+octave:1> @kbd{pkg install -local queueing- at value{VERSION}.tar.gz}
+ at end example
+
+ at quotation Note 
+Octave version 3.2.3 as shipped with Ubuntu 10.04 LTS seems to ignore
+ at option{-local} and always tries to install the package on the system
+directory.
+ at end quotation
+
+ at item
+Verify that the package is indeed installed using the @kbd{pkg list}
+command at the Octave prompt; after succesfull installation you should
+see something like that:
+
+ at example
+octave:1>@kbd{pkg list queueing}
+Package Name  | Version | Installation directory
+--------------+---------+-----------------------
+    queueing  |   @value{VERSION} | /home/moreno/octave/queueing- at value{VERSION}
+ at end example
+
+ at item
+Starting from version 1.1.1, @code{queueing} is no longer
+automatically loaded on Octave startup. To make the functions
+available for use, you need to issue the command
+
+ at example
+octave:1>@kbd{pkg load queueing} 
+ at end example
+
+ at noindent at the Octave prompt. To automatically load @code{queueing} each time
+Octave starts, you can add the command above to the startup script
+(usually, @file{~/.octaverc} on Unix systems).
+
+ at item
+To completely remove @code{queueing} from your system, use the
+ at kbd{pkg uninstall} command:
+
+ at example
+octave:1> @kbd{pkg uninstall queueing}
+ at end example
+
+ at end enumerate
+
+ at c
+ at c
+ at c
+
+ at node Manual installation
+ at section Manual installation
+
+If you want to manually install @code{queueing} in a custom location,
+you can download the tarball and unpack it somewhere:
+
+ at example
+ at kbd{tar xvfz queueing- at value{VERSION}.tar.gz}
+ at kbd{cd queueing- at value{VERSION}/queueing/}
+ at end example
+
+Copy all @code{.m} files from the @file{inst/} directory to some
+target location. Then, start Octave with the @option{-p} option to add
+the target location to the search path, so that Octave will find all
+ at code{queueing} functions automatically:
+
+ at example
+ at kbd{octave -p @emph{/path/to/queueing}}
+ at end example
+
+For example, if all @code{queueing} m-files are in
+ at file{/usr/local/queueing}, you can start Octave as follows:
+
+ at example
+ at kbd{octave -p @emph{/usr/local/queueing}}
+ at end example
+
+If you want, you can add the following line to @file{~/.octaverc}:
+
+ at example
+ at kbd{addpath("@emph{/path/to/queueing}");}
+ at end example
+
+ at noindent so that the path @file{/path/to/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.
+
+ at c
+ at c The following will not appear in the INSTALL text file
+ at c
+ at ifclear INSTALLONLY
+
+ at node Development sources
+ at section Development sources
+
+The source code of the @code{queueing} package can be found in the
+Subversion repository at the URL:
+
+ at url{http://octave.svn.sourceforge.net/viewvc/octave/trunk/octave-forge/main/queueing/}
+
+The source distribution contains additional development files which
+are not present in the installation tarball. This section briefly
+describes the content of the source tree. This is only relevant for
+developers who want to modify the code or documentation; normal users
+of the @code{queueing} package don't need
+
+The source distribution contains the following directories:
+
+ at table @file
+ at item doc/
+Documentation source. Most of the documentation is extracted from the
+comment blocks of individual function files from the @file{inst/}
+directory.
+
+ at 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.
+
+ at item test/
+This directory contains the test functions used to invoke all tests on
+all function files.
+
+ at item devel/
+This directory contains function files which are either not working
+properly, or need additional testing before they are moved to the
+ at file{inst/} directory.
+
+ at 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:
+
+ at table @code
+ at 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
+ at file{doc/queueing.html}, respectively).
+
+ at item check
+Running @samp{make check} will execute all tests contained in the
+ at verb{|m|}-files. If you modify the code of any function in the
+ at 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.
+
+ at item clean
+ at itemx distclean
+ at 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.
+
+ at end table
+
+ at node Naming Conventions
+ at section Naming Conventions
+
+Most of the functions in the @code{queueing} package obey a common
+naming convention. Function names are made of several parts; the first
+part is a prefix which indicates the class of problems the function
+addresses:
+
+ at table @asis
+ at item @strong{ctmc-}
+Functions for continuous-time Markov chains
+
+ at item @strong{dtmc-}
+Functions for discrete-time Markov chains
+
+ at item @strong{qs-}
+Functions for analyzing queueing systems (individual service centers)
+
+ at item @strong{qn-}
+Functions for analyzing queueing networks
+
+ at end table
+
+Functions dealing with Markov chains start with either the @code{ctmc}
+or @code{dtmc} prefix; the prefix is optionally followed by an
+additional string which hints at what the function does:
+
+ at table @asis
+ at item @strong{-bd}
+Birth-Death process
+
+ at item @strong{-mtta}
+Mean Time to Absorption
+
+ at item @strong{-fpt}
+First Passage Times
+
+ at item @strong{-exps}
+Expected Sojourn Times
+
+ at item @strong{-taexps}
+Time-Averaged Expected Sojourn Times
+
+ at end table
+
+For example, function @code{ctmcbd} returns the infinitesimal
+generator matrix for a continuous birth-death process, while
+ at code{dtmcbd} returns the transition probability matrix for a discrete
+birth-death process. Note that there exist functions @code{ctmc} and
+ at code{dtmc} (without any suffix) that compute steady-state and
+transient state occupancy probabilities for CTMCs and DTMCs,
+respectively. @xref{Markov Chains}.
+
+Functions whose name starts with @code{qs-} deal with single station
+queueing systems. The suffix describes the type of system, e.g.,
+ at code{qsmm1} for @math{M/M/1}, @code{qnmmm} for @math{M/M/m} and so
+on. @xref{Single Station Queueing Systems}.
+
+Finally, functions whose name starts with @code{qn-} deal with
+queueing networks. The character that follows indicates whether the
+function handles open (@code{'o'}) or closed (@code{'c'}) networks,
+and whether there is a single customer class (@code{'s'}) or multiple
+classes (@code{'m'}). The string @code{mix} indicates that the
+function supports mixed networks with both open and closed customer
+classes.
+
+ at table @asis
+ at item @strong{-os-}
+Open, single-class network: open network with a single class of customers
+
+ at item @strong{-om-}
+Open, multiclass network: open network with multiple job classes
+
+ at item @strong{-cs-}
+Closed, single-class network
+
+ at item @strong{-cm-}
+Closed, multiclass network
+
+ at item @strong{-mix-}
+Mixed network with open and closed classes of customers
+
+ at end table
+
+The last part of the function name indicates the algorithm implemented
+by the function. @xref{Queueing Networks}.
+
+ at table @asis
+ at item @strong{-aba}
+Asymptotic Bounds Analysis
+
+ at item @strong{-bsb}
+Balanced System Bounds
+
+ at item @strong{-gb}
+Geometric Bounds
+
+ at item @strong{-pb}
+PB Bounds
+
+ at item @strong{-cb}
+Composite Bounds (CB)
+
+ at item @strong{-mva}
+Mean Value Analysis (MVA) algorithm
+
+ at item @strong{-cmva}
+Conditional MVA
+
+ at item @strong{-mvald}
+MVA with general load-dependent servers
+
+ at item @strong{-mvaap}
+Approximate MVA
+
+ at item @strong{-mvablo}
+MVABLO approximation for blocking queueing networks
+
+ at item @strong{-conv}
+Convolution algorithm
+
+ at item @strong{-convld}
+Convolution algorithm with general load-dependent servers
+
+ at end table
+
+ at cindex deprecated functions
+
+The current version (@value{VERSION}) of the @code{queueing} package
+still supports the old function names (although they are no longer
+documented and will disappear in future releases). However, calling
+one of the deprecated functions results in a warning message being
+displayed; the message appears only one time per session:
+
+ at example
+ at group
+octave:1> @kbd{qnclosedab(10,[1 2 3])}
+    @print{} warning: qnclosedab is deprecated. Please use qncsaba instead
+    @result{} ans =  0.16667
+ at end group
+ at end example
+
+Therefore, your legacy code should run unmodified with the current
+version of the @code{queueing} package. You can turn off all warning
+messages with the following command:
+
+ at example
+ at group
+octave:1> @kbd{warning ("off", "qn:deprecated-function");}
+ at end group
+ at end example
+
+However, it is recommended to update to the new API and not ignore the
+warnings above. To help you catch usages of deprecated functions, even
+with applications which produce a lot of console output, you can
+transform warnings into errors so that your application will stop
+immediately:
+
+ at example
+ at group
+octave:1> @kbd{warning ("error", "qn:deprecated-function");}
+ at end group
+ at end example
+
+ at node Quickstart Guide
+ at section Quickstart Guide
+
+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. Extensive
+documentation is provided for each function, and can be displayed with
+the @command{help} command. For example:
+
+ at example
+octave:2> @kbd{help qncsmvablo}
+ at end example
+
+ at noindent prints the documentation for the @command{qncsmvablo} 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}.
+
+Many functions have demo blocks containing code snippets which
+illustrate how that function can be used. For example, to execute the
+demos for the @command{qnclosed} function, use the @command{demo}
+command as follows:
+
+ at example
+octave:4> @kbd{demo qnclosed}
+ at end example
+
+Here we illustrate some basic usage of the @code{queueing} package by
+considering a few examples.
+
+ at noindent @strong{Example 1}
+Compute the stationary state occupancy probabilities of a continuous-time
+Markov chain with infinitesimal generator matrix
+
+ at iftex
+ at tex
+$$
+{\bf Q} =
+\pmatrix{ -0.8 & 0.6  & 9,2 \cr
+           0.3 & -0.7 & 0.4 \cr
+           0.2 & 0.2  & -0.4 }
+$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+    / -0.8   0.6   0.2 \
+Q = |  0.3  -0.7   0.4 |
+    \  0.2   0.2  -0.4 /
+ at end group
+ at end example
+ at end ifnottex
+
+ at example
+ at group
+Q = [ -0.8  0.6  0.2; \
+       0.3 -0.7  0.4; \
+       0.2  0.2 -0.4 ];
+q = ctmc(Q)
+    @result{} q = 0.23256   0.32558   0.44186
+ at end group
+ at end example
+
+ at noindent @strong{Example 2}
+Compute the transient state occupancy probability after @math{n=3}
+transitions of a three state discrete time birth-death process, with
+birth probabilities @math{\lambda_{01} = 0.3} and @math{\lambda_{12} =
+0.5} and death probabilities @math{\mu_{10} = 0.5} and @math{\mu_{21}
+= 0.7}, assuming that the system is initially in state zero (i.e., the
+initial state occupancy probabilities are @math{(1, 0, 0)}).
+
+ at example
+ at group
+n = 3;
+p0 = [1 0 0];
+P = dtmcbd( [0.3 0.5], [0.5 0.7] );
+p = dtmc(P,n,p0)
+    @result{} p = 0.55300   0.29700   0.15000
+ at end group
+ at end example
+
+ at noindent @strong{Example 3}
+Compute server utilization, response time, mean number of requests and
+throughput of a closed queueing network with @math{N=4} requests and
+three @math{M/M/1}--FCFS queues with mean service times @math{{\bf S}
+= (1.0, 0.8, 1.4)} and average number of visits @math{{\bf V} = (1.0,
+0.8, 0.8)}
+
+ at example
+ at group
+S = [1.0 0.8 1.4];
+V = [1.0 0.8 0.8];
+N = 4;
+[U R Q X] = qncsmva(N, S, V)
+    @result{} 
+     U = 0.70064   0.44841   0.78471
+     R = 2.1030    1.2642    3.2433
+     Q = 1.47346   0.70862   1.81792
+     X = 0.70064   0.56051   0.56051
+ at end group
+ at end example
+
+ at noindent @strong{Example 4}
+Compute server utilization, response time, mean number of requests and
+throughput of an open queueing network with three @math{M/M/1}--FCFS
+queues with mean service times @math{{\bf S} = (1.0, 0.8, 1.4)} and
+average number of visits @math{{\bf V} = (1.0, 0.8, 0.8)}. The overall
+arrival rate is @math{\lambda = 0.8} req/s
+
+ at example
+ at group
+S = [1.0 0.8 1.4];
+V = [1.0 0.8 0.8];
+lambda = 0.8;
+[U R Q X] = qnos(lambda, S, V)
+    @result{} 
+     U = 0.80000   0.51200   0.89600
+     R = 5.0000    1.6393   13.4615
+     Q = 4.0000    1.0492    8.6154
+     X = 0.80000   0.64000   0.64000
+ at end group
+ at end example
+
+ at c
+ at c 
+ at c
+ at end ifclear
diff --git a/doc/markovchains.texi b/doc/markovchains.texi
new file mode 100644
index 0000000..b2480c3
--- /dev/null
+++ b/doc/markovchains.texi
@@ -0,0 +1,1427 @@
+ at c This file has been automatically generated from markovchains.txi
+ at c by proc.m. Do not edit this file, all changes will be lost
+
+ at c -*- texinfo -*-
+
+ at c Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014 Moreno Marzolla
+ at c
+ at c This file is part of the queueing package.
+ at c
+ at c The queueing package is free software; you can redistribute it
+ at c and/or modify it under the terms of the GNU General Public License
+ at c as published by the Free Software Foundation; either version 3 of
+ at c the License, or (at your option) any later version.
+ at c
+ at c The queueing package is distributed in the hope that it will be
+ at c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ at c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ at c GNU General Public License for more details.
+ at c
+ at c You should have received a copy of the GNU General Public License
+ at c along with the queueing package; see the file COPYING.  If not, see
+ at c <http://www.gnu.org/licenses/>.
+
+ at node Markov Chains
+ at chapter Markov Chains
+
+ at menu
+* Discrete-Time Markov Chains::
+* Continuous-Time Markov Chains::
+ at end menu
+
+ at node Discrete-Time Markov Chains
+ at section Discrete-Time Markov Chains
+
+Let @math{X_0, X_1, @dots{}, X_n, @dots{} } be a sequence of random
+variables defined over a discete state space @math{1, 2,
+ at dots{}}. The sequence @math{X_0, X_1, @dots{}, X_n, @dots{}}  is a
+ at emph{stochastic process} with discrete time @math{0, 1, 2,
+ at dots{}}. A @emph{Markov chain} is a stochastic process @math{@{X_n,
+n=0, 1, 2, @dots{}@}} which satisfies the following Markov property:
+
+ at iftex
+ at tex
+$$\eqalign{P\left(X_{n+1} = x_{n+1}\ |\ X_n = x_n, X_{n-1} = x_{n-1}, \ldots, X_0 = x_0 \right) \cr
+& = P\left(X_{n+1} = x_{n+1}\ |\ X_n = x_n\right)}$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at math{P(X_{n+1} = x_{n+1} | X_n = x_n, X_{n-1} = x_{n-1}, ..., X_0 = x_0) = P(X_{n+1} = x_{n+1} | X_n = x_n)}
+ at end ifnottex
+
+ at noindent which basically means that the probability that the system is in
+a particular state at time @math{n+1} only depends on the state the
+system was at time @math{n}.
+
+The evolution of a Markov chain with finite state space @math{@{1, 2,
+ at dots{}, N@}} can be fully described by a stochastic matrix @math{{\bf
+P}(n) = [ P_{i,j}(n) ]} such that @math{P_{i, j}(n) = P( X_{n+1} = j\
+|\ X_n = i )}.  If the Markov chain is homogeneous (that is, the
+transition probability matrix @math{{\bf P}(n)} is time-independent),
+we can write @math{{\bf P} = [P_{i, j}]}, where @math{P_{i, j} = P(
+X_{n+1} = j\ |\ X_n = i )} for all @math{n=0, 1, @dots{}}.
+
+The transition probability matrix @math{\bf P} must satisfy the
+following two properties: (1) @math{P_{i, j} @geq{} 0} for all
+ at math{i, j}, and (2) @math{\sum_{j=1}^N P_{i,j} = 1} for all @math{i}
+
+ at c
+ at anchor{doc-dtmcchkP}
+
+
+ at deftypefn {Function File} {[@var{r} @var{err}] =} dtmcchkP (@var{P})
+
+ at cindex Markov chain, discrete time
+ at cindex DTMC
+ at cindex discrete time Markov chain
+
+Check whether @var{P} is a valid transition probability matrix. 
+
+If @var{P} is valid, @var{r} is the size (number of rows or columns)
+of @var{P}. If @var{P} is not a transition probability matrix,
+ at var{r} is set to zero, and @var{err} to an appropriate error string.
+
+ at end deftypefn
+
+
+ at menu
+* State occupancy probabilities (DTMC)::
+* Birth-death process (DTMC)::
+* Expected number of visits (DTMC)::
+* Time-averaged expected sojourn times (DTMC)::
+* Mean time to absorption (DTMC)::
+* First passage times (DTMC)::
+ at end menu
+
+ at c
+ at c
+ at c
+ at node State occupancy probabilities (DTMC)
+ at subsection State occupancy probabilities
+
+Given a discrete-time Markov chain with state spate @math{@{1, 2,
+ at dots{}, N@}}, we denote with @math{{\bf \pi}(n) = \left(\pi_1(n),
+\pi_2(n), @dots{}, \pi_N(n) \right)} the @emph{state occupancy
+probability vector} at step @math{n}, @math{n = 0, 1, @dots{}}.
+ at math{\pi_i(n)} denotes the probability that the system is in state
+ at math{i} after @math{n} transitions.
+
+Given the transition probability matrix @math{\bf P} and the initial
+state occupancy probability vector @math{{\bf \pi}(0) =
+\left(\pi_1(0), \pi_2(0), @dots{}, \pi_N(0)\right)}, @math{{\bf
+\pi}(n)} can be computed as:
+
+ at iftex
+ at tex
+$${\bf \pi}(n) = {\bf \pi}(0) {\bf P}^n$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+\pi(n) = \pi(0) P^n
+ at end group
+ at end example
+ at end ifnottex
+
+Under certain conditions, there exists a @emph{stationary state
+occupancy probability} @math{{\bf \pi} = \lim_{n \rightarrow +\infty}
+{\bf \pi}(n)}, which is independent from @math{{\bf \pi}(0)}. The
+stationary vector @math{\bf \pi} is the solution of the following
+linear system:
+
+ at iftex
+ at tex
+$$
+\left\{ \eqalign{
+{\bf \pi P} & = {\bf \pi} \cr
+{\bf \pi 1}^T & = 1
+} \right.
+$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+/
+| \pi P   = \pi
+| \pi 1^T = 1
+\
+ at end group
+ at end example
+ at end ifnottex
+
+ at noindent where @math{\bf 1} is the row vector of ones, and @math{( \cdot )^T}
+the transpose operator.
+
+ at c
+ at anchor{doc-dtmc}
+
+
+ at deftypefn {Function File} {@var{p} =} dtmc (@var{P})
+ at deftypefnx {Function File} {@var{p} =} dtmc (@var{P}, @var{n}, @var{p0})
+
+ at cindex Markov chain, discrete time
+ at cindex discrete time Markov chain
+ at cindex DTMC
+ at cindex Markov chain, stationary probabilities
+ at cindex Markov chain, transient probabilities
+
+Compute stationary or transient state occupancy probabilities for a discrete-time Markov chain.
+
+With a single argument, compute the stationary state occupancy
+probability vector @code{@var{p}(1), @dots{}, @var{p}(N)} for a
+discrete-time Markov chain with state space @math{@{1, 2, @dots{},
+N@}} and with @math{N \times N} transition probability matrix
+ at var{P}. With three arguments, compute the transient state occupancy
+vector @code{@var{p}(1), @dots{}, @var{p}(N)} that the system is in
+state @math{i} after @var{n} steps, given initial occupancy
+probabilities @var{p0}(1), @dots{}, @var{p0}(N).
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item P
+ at 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.
+
+ at item n
+Number of transitions after which compute the state occupancy probabilities
+(@math{n=0, 1, @dots{}})
+
+ at item p0
+ at code{@var{p0}(i)} is the probability that at step 0 the system
+is in state @math{i}.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item p
+If this function is called with a single argument, @code{@var{p}(i)}
+is the steady-state probability that the system is in state @math{i}.
+If this function is called with three arguments, @code{@var{p}(i)}
+is the probability that the system is in state @math{i}
+after @var{n} transitions, given the initial probabilities
+ at code{@var{p0}(i)} that the initial state is @math{i}.
+
+ at end table
+
+ at seealso{ctmc}
+
+ at end deftypefn
+
+
+ at noindent @strong{EXAMPLE}
+
+This example is from @ref{GrSn97}. Let us consider a maze with nine
+rooms, as shown in the following figure
+
+ at example
+ at group
++-----+-----+-----+
+|     |     |     |
+|  1     2     3  |
+|     |     |     |
++-   -+-   -+-   -+
+|     |     |     |
+|  4     5     6  |
+|     |     |     |
++-   -+-   -+-   -+
+|     |     |     |
+|  7     8     9  |
+|     |     |     |
++-----+-----+-----+
+ at end group
+ at end example
+
+A mouse is placed in one of the rooms and can wander around. At each
+step, the mouse moves from the current room to a neighboring one with
+equal probability: if it is in room 1, it can move to room 2 and 4
+with probability 1/2, respectively. If the mouse is in room 8, it can
+move to either 7, 5 or 9 with probability 1/3.
+
+The transition probability @math{\bf P} from room @math{i} to room
+ at math{j} is the following:
+
+ at iftex
+ at tex
+$$ {\bf P} = 
+\pmatrix{ 0   & 1/2 & 0   & 1/2 & 0   & 0   & 0   & 0   & 0   \cr
+          1/3 & 0   & 1/3 & 0   & 1/3 & 0   & 0   & 0   & 0   \cr
+          0   & 1/2 & 0   & 0   & 0   & 1/2 & 0   & 0   & 0   \cr
+          1/3 & 0   & 0   & 0   & 1/3 & 0   & 1/3 & 0   & 0   \cr
+          0   & 1/4 & 0   & 1/4 & 0   & 1/4 & 0   & 1/4 & 0   \cr
+          0   & 0   & 1/3 & 0   & 1/3 & 0   & 0   & 0   & 1/3 \cr
+          0   & 0   & 0   & 1/2 & 0   & 0   & 0   & 1/2 & 0   \cr
+          0   & 0   & 0   & 0   & 1/3 & 0   & 1/3 & 0   & 1/3 \cr
+          0   & 0   & 0   & 0   & 0   & 1/2 & 0   & 1/2 & 0   }
+$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+        / 0     1/2   0     1/2   0     0     0     0     0   \
+        | 1/3   0     1/3   0     1/3   0     0     0     0   |
+        | 0     1/2   0     0     0     1/2   0     0     0   |
+        | 1/3   0     0     0     1/3   0     1/3   0     0   |
+    P = | 0     1/4   0     1/4   0     1/4   0     1/4   0   |
+        | 0     0     1/3   0     1/3   0     0     0     1/3 |
+        | 0     0     0     1/2   0     0     0     1/2   0   |
+        | 0     0     0     0     1/3   0     1/3   0     1/3 |
+        \ 0     0     0     0     0     1/2   0     1/2   0   /
+ at end group
+ at end example
+ at end ifnottex
+
+The stationary state occupancy probability vector can be computed
+using the following code:
+
+ at example
+ at group
+ at verbatim
+ P = zeros(9,9);
+ P(1,[2 4]    ) = 1/2;
+ P(2,[1 5 3]  ) = 1/3;
+ P(3,[2 6]    ) = 1/2;
+ P(4,[1 5 7]  ) = 1/3;
+ P(5,[2 4 6 8]) = 1/4;
+ P(6,[3 5 9]  ) = 1/3;
+ P(7,[4 8]    ) = 1/2;
+ P(8,[7 5 9]  ) = 1/3;
+ P(9,[6 8]    ) = 1/2;
+ p = dtmc(P);
+ disp(p)
+ at end verbatim
+ at end group
+    @result{} 0.083333   0.125000   0.083333   0.125000   
+       0.166667   0.125000   0.083333   0.125000   
+       0.083333
+ at end example
+
+ at c
+ at node Birth-death process (DTMC)
+ at subsection Birth-death process
+
+ at anchor{doc-dtmcbd}
+
+
+ at deftypefn {Function File} {@var{P} =} dtmcbd (@var{b}, @var{d})
+
+ at cindex Markov chain, discrete time
+ at cindex DTMC
+ at cindex discrete time Markov chain
+ at cindex birth-death process, DTMC
+
+Returns the transition probability matrix @math{P} for a discrete
+birth-death process over state space @math{1, 2, @dots{}, N}.
+ at code{@var{b}(i)} is the transition probability from state
+ at math{i} to @math{i+1}, and @code{@var{d}(i)} is the transition
+probability from state @math{i+1} to state @math{i}, @math{i=1, 2,
+ at dots{}, N-1}.
+
+Matrix @math{\bf P} is therefore defined as:
+
+ at tex
+$$ \pmatrix{ (1-\lambda_1) & \lambda_1 & & & & \cr
+             \mu_1 & (1 - \mu_1 - \lambda_2) & \lambda_2 & & \cr
+             & \mu_2 & (1 - \mu_2 - \lambda_3) & \lambda_3 & & \cr
+             \cr
+             & & \ddots & \ddots & \ddots & & \cr
+             \cr
+             & & & \mu_{N-2} & (1 - \mu_{N-2}-\lambda_{N-1}) & \lambda_{N-1} \cr
+             & & & & \mu_{N-1} & (1-\mu_{N-1}) }
+$$
+ at end tex
+ at ifnottex
+ at example
+ at group
+/                                                             \
+| 1-b(1)     b(1)                                             |
+|  d(1)  (1-d(1)-b(2))     b(2)                               |
+|            d(2)      (1-d(2)-b(3))     b(3)                 |
+|                                                             |
+|                 ...           ...          ...              |
+|                                                             |
+|                         d(N-2)   (1-d(N-2)-b(N-1))  b(N-1)  |
+|                                        d(N-1)      1-d(N-1) |
+\                                                             /
+ at end group
+ at end example
+ at end ifnottex
+
+ at noindent where @math{\lambda_i} and @math{\mu_i} are the birth and
+death probabilities, respectively.
+
+ at seealso{ctmcbd}
+
+ at end deftypefn
+
+
+ at c
+ at node Expected number of visits (DTMC)
+ at subsection Expected Number of Visits
+
+Given a @math{N} state discrete-time Markov chain with transition
+matrix @math{\bf P} and an integer @math{n @geq{} 0}, we let
+ at math{L_i(n)} be the the expected number of visits to state @math{i}
+during the first @math{n} transitions. The vector @math{{\bf L}(n) =
+( L_1(n), L_2(n), @dots{}, L_N(n) )} is defined as
+
+ at iftex
+ at tex
+$$ {\bf L}(n) = \sum_{i=0}^n {\bf \pi}(i) = \sum_{i=0}^n {\bf \pi}(0) {\bf P}^i $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+         n            n
+        ___          ___
+       \            \           i
+L(n) =  >   pi(i) =  >   pi(0) P
+       /___         /___
+        i=0          i=0
+ at end group
+ at end example
+ at end ifnottex
+
+ at noindent where @math{{\bf \pi}(i) = {\bf \pi}(0){\bf P}^i} is the state 
+occupancy probability after @math{i} transitions.
+
+If @math{\bf P} is absorbing, i.e., the stochastic process eventually
+reaches a state with no outgoing transitions with probability 1, then
+we can compute the expected number of visits until absorption
+ at math{\bf L}. To do so, we first rearrange the states to rewrite
+matrix @math{\bf P} as:
+
+ at iftex
+ at tex
+$$ {\bf P} = \pmatrix{ {\bf Q} & {\bf R} \cr
+                       {\bf 0} & {\bf I} }$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+    / Q | R \
+P = |---+---|
+    \ 0 | I /
+ at end group
+ at end example
+ at end ifnottex
+
+ at noindent where the first @math{t} states are transient
+and the last @math{r} states are absorbing (@math{t+r = N}). The
+matrix @math{{\bf N} = ({\bf I} - {\bf Q})^{-1}} is called the
+ at emph{fundamental matrix}; @math{N_{i,j}} is the expected number of
+times that the process is in the @math{j}-th transient state if it
+started in the @math{i}-th transient state. If we reshape @math{\bf N}
+to the size of @math{\bf P} (filling missing entries with zeros), we
+have that, for absorbing chains @math{{\bf L} = {\bf \pi}(0){\bf N}}.
+
+ at anchor{doc-dtmcexps}
+
+
+ at deftypefn {Function File} {@var{L} =} dtmcexps (@var{P}, @var{n}, @var{p0})
+ at deftypefnx {Function File} {@var{L} =} dtmcexps (@var{P}, @var{p0})
+
+ at cindex expected sojourn times, DTMC
+ at cindex DTMC
+ at cindex discrete time Markov chain
+ at cindex Markov chain, discrete time
+
+Compute the expected number of visits to each state during the first
+ at var{n} transitions, or until abrosption.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item P
+ at math{N \times N} transition probability matrix.
+
+ at item n
+Number of steps during which the expected number of visits are
+computed (@math{@var{n} @geq{} 0}). If @code{@var{n}=0}, returns
+ at var{p0}. If @code{@var{n} > 0}, returns the expected number of
+visits after exactly @var{n} transitions.
+
+ at item p0
+Initial state occupancy probability.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item L
+When called with two arguments, @code{@var{L}(i)} is the expected
+number of visits to transient state @math{i} before absorption. When
+called with three arguments, @code{@var{L}(i)} is the expected number
+of visits to state @math{i} during the first @var{n} transitions,
+given initial occupancy probability @var{p0}.
+
+ at end table
+
+ at seealso{ctmcexps}
+
+ at end deftypefn
+
+
+ at c
+ at node Time-averaged expected sojourn times (DTMC)
+ at subsection Time-averaged expected sojourn times
+
+ at anchor{doc-dtmctaexps}
+
+
+ at deftypefn {Function File} {@var{L} =} dtmctaexps (@var{P}, @var{n}, @var{p0})
+ at deftypefnx {Function File} {@var{L} =} dtmctaexps (@var{P}, @var{p0})
+
+ at cindex time-alveraged sojourn time, DTMC
+ at cindex discrete time Markov chain
+ at cindex Markov chain, discrete time
+ at cindex DTMC
+
+Compute the @emph{time-averaged sojourn time} @code{@var{M}(i)},
+defined as the fraction of time steps @math{@{0, 1, @dots{}, n@}} (or
+until absorption) spent in state @math{i}, assuming that the state
+occupancy probabilities at time 0 are @var{p0}.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item P
+ at math{N \times N} transition probability matrix.
+
+ at item n
+Number of transitions during which the time-averaged expected sojourn times
+are computed (@math{@var{n} @geq{} 0}). if @math{@var{n} = 0},
+returns @var{p0}.
+
+ at item p0
+Initial state occupancy probabilities.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item M
+If this function is called with three arguments, @code{@var{M}(i)} is
+the expected fraction of steps @math{@{0, 1, @dots{}, n@}} spent in
+state @math{i}, assuming that the state occupancy probabilities at
+time zero are @var{p0}. If this function is called with two
+arguments, @code{@var{M}(i)} is the expected fraction of steps spent
+in state @math{i} until absorption.
+
+ at end table
+
+ at seealso{ctmctaexps}
+
+ at end deftypefn
+
+
+ at c
+ at node Mean time to absorption (DTMC)
+ at subsection Mean Time to Absorption
+
+The @emph{mean time to absorption} is defined as the average number of
+transitions which are required to reach an absorbing state, starting
+from a transient state (or given an initial state occupancy
+probability vector @math{{\bf \pi}(0)}). 
+
+Let @math{{\bf t}_i} be the expected number of transitions before
+being absorbed in any absorbing state, starting from state @math{i}.
+Vector @math{\bf t} can be computed from the fundamental matrix
+ at math{\bf N} (@pxref{Expected number of visits (DTMC)}) as
+
+ at iftex
+ at tex
+$$ {\bf t} = {\bf 1 N} $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+t = 1 N
+ at end example
+ at end ifnottex
+
+Let @math{{\bf B} = [ B_{i, j} ]} be a matrix where @math{B_{i, j}} is
+the probability of being absorbed in state @math{j}, starting from
+transient state @math{i}. Again, using matrices @math{\bf N} and
+ at math{\bf R} (@pxref{Expected number of visits (DTMC)}) we can write
+
+ at iftex
+ at tex
+$$ {\bf B} = {\bf N R} $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+B = N R
+ at end example
+ at end ifnottex
+
+ at anchor{doc-dtmcmtta}
+
+
+ at deftypefn {Function File} {[@var{t} @var{N} @var{B}] =} dtmcmtta (@var{P})
+ at deftypefnx {Function File} {[@var{t} @var{N} @var{B}] =} dtmcmtta (@var{P}, @var{p0})
+
+ at cindex mean time to absorption, DTMC
+ at cindex absorption probabilities, DTMC
+ at cindex fundamental matrix
+ at cindex DTMC
+ at cindex discrete time Markov chain
+ at cindex Markov chain, discrete time
+
+Compute the expected number of steps before absorption for a
+DTMC with @math{N \times N} transition probability matrix @var{P};
+compute also the fundamental matrix @var{N} for @var{P}.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item P
+ at math{N \times N} transition probability matrix.
+
+ at end table
+
+ at strong{OUTPUTS} 
+
+ at table @var
+
+ at item t
+When called with a single argument, @var{t} is a vector of size
+ at math{N} such that @code{@var{t}(i)} is the expected number of steps
+before being absorbed in any absorbing state, starting from state
+ at math{i}; if @math{i} is absorbing, @code{@var{t}(i) = 0}. When
+called with two arguments, @var{t} is a scalar, and represents the
+expected number of steps before absorption, starting from the initial
+state occupancy probability @var{p0}.
+
+ at item N
+When called with a single argument, @var{N} is the @math{N \times N}
+fundamental matrix for @var{P}. @code{@var{N}(i,j)} is the expected
+number of visits to transient state @var{j} before absorption, if it
+is started in transient state @var{i}. The initial state is counted
+if @math{i = j}. When called with two arguments, @var{N} is a vector
+of size @math{N} such that @code{@var{N}(j)} is the expected number
+of visits to transient state @var{j} before absorption, given initial
+state occupancy probability @var{P0}.
+
+ at item B
+When called with a single argument, @var{B} is a @math{N \times N}
+matrix where @code{@var{B}(i,j)} is the probability of being absorbed
+in state @math{j}, starting from transient state @math{i}; if
+ at math{j} is not absorbing, @code{@var{B}(i,j) = 0}; if @math{i}
+is absorbing, @code{@var{B}(i,i) = 1} and
+ at code{@var{B}(i,j) = 0} for all @math{j \neq j}. When called with
+two arguments, @var{B} is a vector of size @math{N} where
+ at code{@var{B}(j)} is the probability of being absorbed in state
+ at var{j}, given initial state occupancy probabilities @var{p0}.
+
+ at end table
+
+ at seealso{ctmcmtta}
+
+ at end deftypefn
+
+
+ at c
+ at node First passage times (DTMC)
+ at subsection First Passage Times
+
+The First Passage Time @math{M_{i, j}} is 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
+
+ at iftex
+ at tex
+$$ M_{i, j} = 1 + \sum_{k \neq j} P_{i, k} M_{k, j}$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+           ___
+          \
+M_ij = 1 + >   P_ij * M_kj
+          /___
+          k!=j
+ at end group
+ at end example
+ at end ifnottex
+
+To compute @math{{\bf M} = [ M_{i, j}]} a different formulation is
+used.  Let @math{\bf W} be the @math{N \times N} matrix having each
+row equal to the stationary state occupancy probability vector
+ at math{\bf \pi} for @math{\bf P}; let @math{\bf I} be the @math{N
+\times N} identity matrix. Define @math{\bf Z} as follows:
+
+ at iftex
+ at tex
+$$ {\bf Z} = \left( {\bf I} - {\bf P} + {\bf W} \right)^{-1} $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+               -1
+Z = (I - P + W)
+ at end group
+ at end example
+ at end ifnottex
+
+ at noindent Then, we have that
+
+ at iftex
+ at tex
+$$ M_{i, j} = {Z_{j, j} - Z_{i, j} \over \pi_j} $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+       Z_jj - Z_ij
+M_ij = -----------
+          \pi_j
+ at end group
+ at end example
+ at end ifnottex
+
+According to the definition above, @math{M_{i,i} = 0}. We arbitrarily
+let @math{M_{i,i}} to be the @emph{mean recurrence time} @math{r_i}
+for state @math{i}, that is the average number of transitions needed
+to return to state @math{i} starting from it. @math{r_i} is:
+
+ at iftex
+ at tex
+$$ r_i = {1 \over \pi_i} $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+        1
+r_i = -----
+      \pi_i
+ at end group
+ at end example
+ at end ifnottex
+
+ at anchor{doc-dtmcfpt}
+
+
+ at deftypefn {Function File} {@var{M} =} dtmcfpt (@var{P})
+
+ at cindex first passage times
+ at cindex mean recurrence times
+ at cindex discrete time Markov chain
+ at cindex Markov chain, discrete time
+ at cindex DTMC
+
+Compute mean first passage times and mean recurrence times
+for an irreducible discrete-time Markov chain.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item P
+ at 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.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item M
+For all @math{i \neq j}, @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}. @code{@var{M}(i,i)} is the @emph{mean
+recurrence time} of state @math{i}, and represents the average time
+needed to return to state @var{i}.
+
+ at end table
+
+ at seealso{ctmcfpt}
+
+ at end deftypefn
+
+
+ at c
+ at c
+ at c
+ at node Continuous-Time Markov Chains
+ at section Continuous-Time Markov Chains
+
+A stochastic process @math{@{X(t), t @geq{} 0@}} is a continuous-time
+Markov chain if, for all integers @math{n}, and for any sequence
+ at math{t_0, t_1 , \ldots, t_n, t_{n+1}} such that @math{t_0 < t_1 <
+\ldots < t_n < t_{n+1}}, we have
+
+ at iftex
+ at tex
+$$\eqalign{P(X(t_{n+1}) = x_{n+1}\ |\ X(t_n) = x_n, X(t_{n-1}) = x_{n-1}, \ldots, X(t_0) = x_0) \cr
+&= P(X(t_{n+1}) = x_{n+1}\ |\ X(t_n) = x_n)}$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at math{P(X_{n+1} = x_{n+1} | X_n = x_n, X_{n-1} = x_{n-1}, ..., X_0 = x_0) = P(X_{n+1} = x_{n+1} | X_n = x_n)}
+ at end ifnottex
+
+A continuous-time Markov chain is defined according to an
+ at emph{infinitesimal generator matrix} @math{{\bf Q} = [Q_{i,j}]},
+where for each @math{i \neq j}, @math{Q_{i, j}} is the transition rate
+from state @math{i} to state @math{j}. The matrix @math{\bf Q} must
+satisfy the property that, for all @math{i}, @math{\sum_{j=1}^N Q_{i,
+j} = 0}.
+
+ at anchor{doc-ctmcchkQ}
+
+
+ at deftypefn {Function File} {[@var{result} @var{err}] =} ctmcchkQ (@var{Q})
+
+ at cindex Markov chain, continuous time
+
+If @var{Q} is a valid infinitesimal generator matrix, return
+the size (number of rows or columns) of @var{Q}. If @var{Q} is not
+an infinitesimal generator matrix, set @var{result} to zero, and
+ at var{err} to an appropriate error string.
+
+ at end deftypefn
+
+
+ at menu
+* State occupancy probabilities (CTMC)::
+* Birth-death process (CTMC)::
+* Expected sojourn times (CTMC)::
+* Time-averaged expected sojourn times (CTMC)::
+* Mean time to absorption (CTMC)::
+* First passage times (CTMC)::
+ at end menu
+
+ at node State occupancy probabilities (CTMC)
+ at subsection State occupancy probabilities
+
+Similarly to the discrete case, we denote with @math{{\bf \pi}(t) =
+(\pi_1(t), \pi_2(t), @dots{}, \pi_N(t) )} the @emph{state occupancy
+probability vector} at time @math{t}. @math{\pi_i(t)} is the
+probability that the system is in state @math{i} at time @math{t
+ at geq{} 0}.
+
+Given the infinitesimal generator matrix @math{\bf Q} and the initial
+state occupancy probabilities @math{{\bf \pi}(0) = (\pi_1(0),
+\pi_2(0), @dots{}, \pi_N(0))}, the state occupancy probabilities
+ at math{{\bf \pi}(t)} at time @math{t} can be computed as:
+
+ at iftex
+ at tex
+$${\bf \pi}(t) = {\bf \pi}(0) \exp( {\bf Q} t )$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+\pi(t) = \pi(0) exp(Qt)
+ at end group
+ at end example
+ at end ifnottex
+
+ at noindent where @math{\exp( {\bf Q} t )} is the matrix exponential
+of @math{{\bf Q} t}. Under certain conditions, there exists a
+ at emph{stationary state occupancy probability} @math{{\bf \pi} =
+\lim_{t \rightarrow +\infty} {\bf \pi}(t)}, which is independent from
+ at math{{\bf \pi}(0)}.  @math{\bf \pi} is the solution of the following
+linear system:
+
+ at iftex
+ at tex
+$$
+\left\{ \eqalign{
+{\bf \pi Q} & = {\bf 0} \cr
+{\bf \pi 1}^T & = 1
+} \right.
+$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+/
+| \pi Q   = 0
+| \pi 1^T = 1
+\
+ at end group
+ at end example
+ at end ifnottex
+
+ at anchor{doc-ctmc}
+
+
+ at deftypefn {Function File} {@var{p} =} ctmc (@var{Q})
+ at deftypefnx {Function File} {@var{p} =} ctmc (@var{Q}, @var{t}. @var{p0})
+
+ at cindex Markov chain, continuous time
+ at cindex continuous time Markov chain
+ at cindex Markov chain, state occupancy probabilities
+ at cindex stationary probabilities
+ at cindex CTMC
+
+Compute stationary or transient state occupancy probabilities for a continuous-time Markov chain.
+
+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 state space @math{@{1, 2, @dots{},
+N@}} and @math{N \times N} infinitesimal generator matrix @var{Q}.
+With three arguments, compute the state occupancy probabilities
+ at var{p}(1), @dots{}, @var{p}(N) that the system is in state @math{i}
+at time @var{t}, given initial state occupancy probabilities
+ at var{p0}(1), @dots{}, @var{p0}(N) at time 0.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at 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
+ at math{i} to state @math{j}, for @math{1 @leq{} i \neq j @leq{} N}.
+#var{Q} must satisfy the property that @math{\sum_{j=1}^N Q_{i, j} =
+0}
+
+ at item t
+Time at which to compute the transient probability (@math{t @geq{}
+0}). If omitted, the function computes the steady state occupancy
+probability vector.
+
+ at item p0
+ at code{@var{p0}(i)} is the probability that the system
+is in state @math{i} at time 0.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at 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},
+ at math{i = 1, @dots{}, N}. 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{p0}(1), @dots{}, @var{p0}(N).
+
+ at end table
+
+ at seealso{dtmc}
+
+ at end deftypefn
+
+
+ at noindent @strong{EXAMPLE}
+
+Consider a two-state CTMC such that transition rates between states
+are equal to 1. This can be solved as follows:
+
+ at example
+ at group
+ at verbatim
+ Q = [ -1  1; ...
+        1 -1  ];
+ q = ctmc(Q)
+ at end verbatim
+    @result{} q = 0.50000   0.50000
+ at end group
+ at end example
+
+ at c
+ at c
+ at c
+ at node Birth-death process (CTMC)
+ at subsection Birth-Death Process
+
+ at anchor{doc-ctmcbd}
+
+
+ at deftypefn {Function File} {@var{Q} =} ctmcbd (@var{b}, @var{d})
+
+ at cindex Markov chain, continuous time
+ at cindex continuous time Markov chain
+ at cindex CTMC
+ at cindex birth-death process, CTMC
+
+Returns the infinitesimal generator matrix @math{Q} for a continuous
+birth-death process over state space @math{1, 2, @dots{}, N}.
+ at code{@var{b}(i)} is the transition rate from state @math{i} to
+ at math{i+1}, and @code{@var{d}(i)} is the transition rate from state
+ at math{i+1} to state @math{i}, @math{i=1, 2, @dots{}, N-1}.
+
+Matrix @math{\bf Q} is therefore defined as:
+
+ at tex
+$$ \pmatrix{ -\lambda_1 & \lambda_1 & & & & \cr
+             \mu_1 & -(\mu_1 + \lambda_2) & \lambda_2 & & \cr
+             & \mu_2 & -(\mu_2 + \lambda_3) & \lambda_3 & & \cr
+             \cr
+             & & \ddots & \ddots & \ddots & & \cr
+             \cr
+             & & & \mu_{N-2} & -(\mu_{N-2}+\lambda_{N-1}) & \lambda_{N-1} \cr
+             & & & & \mu_{N-1} & -\mu_{N-1} }
+$$
+ at end tex
+ at ifnottex
+
+ at example
+ at group
+/                                                          \
+| -b(1)     b(1)                                           |
+|  d(1) -(d(1)+b(2))     b(2)                              |
+|           d(2)     -(d(2)+b(3))        b(3)              |
+|                                                          |
+|                ...           ...          ...            |
+|                                                          |
+|                       d(N-2)    -(d(N-2)+b(N-1))  b(N-1) |
+|                                       d(N-1)     -d(N-1) |
+\                                                          /
+ at end group
+ at end example
+ at end ifnottex
+
+ at noindent where @math{\lambda_i} and @math{\mu_i} are the birth and
+death rates, respectively.
+
+ at seealso{dtmcbd}
+
+ at end deftypefn
+
+
+ at c
+ at c
+ at c
+ at node Expected sojourn times (CTMC)
+ at subsection Expected Sojourn Times
+
+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
+ at math{[0,t)}, assuming that the initial occupancy probability at time
+0 was @math{{\bf \pi}(0)}. @math{{\bf L}(t)} can be expressed as the
+solution of the following differential equation:
+
+ at iftex
+ at tex
+$$ { d{\bf L}(t) \over dt} = {\bf L}(t){\bf Q} + {\bf \pi}(0), \qquad {\bf L}(0) = {\bf 0} $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+ dL
+ --(t) = L(t) Q + pi(0),    L(0) = 0
+ dt
+ at end group
+ at end example
+ at end ifnottex
+
+Alternatively, @math{{\bf L}(t)} can also be expressed in integral
+form as:
+
+ at iftex
+ at tex
+$$ {\bf L}(t) = \int_0^t {\bf \pi}(u) du$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+       / t
+L(t) = |   pi(u) du
+       / 0
+ at end group
+ at end example
+ at end ifnottex
+
+ at noindent where @math{{\bf \pi}(t) = {\bf \pi}(0) \exp({\bf Q}t)} is
+the state occupancy probability at time @math{t}; @math{\exp({\bf Q}t)}
+is the matrix exponential of @math{{\bf Q}t}.
+
+If there are absorbing states, we can define the vector @emph{expected
+sojourn times until absorption} @math{{\bf L}(\infty)}, where for each
+transient state @math{i}, @math{L_i(\infty)} is the expected total
+time spent in state @math{i} until absorption, assuming that the
+system started with a given state occupancy probability vector
+ at math{{\bf \pi}(0)}. Let @math{\tau} be the set of transient (i.e.,
+non absorbing) states; let @math{{\bf Q}_\tau} be the restriction of
+ at math{\bf Q} to the transient substates only. Similarly, let
+ at math{{\bf \pi}_\tau(0)} be the restriction of the initial probability
+vector @math{{\bf \pi}(0)} to transient states @math{\tau}.
+
+The expected time to absorption @math{{\bf L}_\tau(\infty)} is defined as
+the solution of the following equation:
+
+ at iftex
+ at tex
+$$ {\bf L}_\tau(\infty){\bf Q}_\tau = -{\bf \pi}_\tau(0) $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+L_T( inf ) Q_T = -pi_T(0)
+ at end group
+ at end example
+ at end ifnottex
+
+ at anchor{doc-ctmcexps}
+
+
+ at deftypefn {Function File} {@var{L} =} ctmcexps (@var{Q}, @var{t}, @var{p} )
+ at deftypefnx {Function File} {@var{L} =} ctmcexps (@var{Q}, @var{p})
+
+ at cindex Markov chain, continuous time
+ at cindex expected sojourn time, CTMC
+
+With three arguments, compute the expected times @code{@var{L}(i)}
+spent in each state @math{i} during the time interval @math{[0,t]},
+assuming that the initial occupancy vector is @var{p}. With two
+arguments, compute the expected time @code{@var{L}(i)} spent in each
+transient state @math{i} until absorption.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item Q
+ at 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{1
+ at leq{} i \neq j @leq{} N}. The matrix @var{Q} must also satisfy the
+condition @math{\sum_{j=1}^N Q_{ij} = 0}.
+
+ at item t
+If given, compute the expected sojourn times in @math{[0,t]}
+
+ at item p
+Initial occupancy probability vector; @code{@var{p}(i)} is the
+probability the system is in state @math{i} at time 0, @math{i = 1,
+ at dots{}, N}
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item L
+If this function is called with three arguments, @code{@var{L}(i)} is
+the expected time spent in state @math{i} during the interval
+ at math{[0,t]}. If this function is called with two arguments
+ at code{@var{L}(i)} is the expected time spent in transient state
+ at math{i} until absorption; if state @math{i} is absorbing,
+ at code{@var{L}(i)} is zero.
+
+ at end table
+
+ at seealso{dtmcexps}
+
+ at end deftypefn
+
+
+ at 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{{\bf \pi}_0=(1,0,0,0)}.
+
+ at example
+ at group
+ at verbatim
+ lambda = 0.5;
+ N = 4;
+ b = lambda*[1:N-1];
+ d = zeros(size(b));
+ Q = ctmcbd(b,d);
+ t = linspace(0,10,100);
+ p0 = zeros(1,N); p0(1)=1;
+ L = zeros(length(t),N);
+ for i=1:length(t)
+   L(i,:) = ctmcexps(Q,t(i),p0);
+ endfor
+ plot( t, L(:,1), ";State 1;", "linewidth", 2, ...
+       t, L(:,2), ";State 2;", "linewidth", 2, ...
+       t, L(:,3), ";State 3;", "linewidth", 2, ...
+       t, L(:,4), ";State 4;", "linewidth", 2 );
+ legend("location","northwest");
+ xlabel("Time");
+ ylabel("Expected sojourn time");
+ at end verbatim
+ at end group
+ at end example
+
+ at c
+ at c
+ at c
+ at node Time-averaged expected sojourn times (CTMC)
+ at subsection Time-Averaged Expected Sojourn Times
+
+ at anchor{doc-ctmctaexps}
+
+
+ at deftypefn {Function File} {@var{M} =} ctmctaexps (@var{Q}, @var{t}, @var{p})
+ at deftypefnx {Function File} {@var{M} =} ctmctaexps (@var{Q}, @var{p})
+
+ at cindex Markov chain, continuous time
+ at cindex time-alveraged sojourn time, CTMC
+ at cindex continuous time Markov chain
+ at cindex CTMC
+
+Compute the @emph{time-averaged sojourn time} @code{@var{M}(i)},
+defined as the fraction of the time interval @math{[0,t]} (or until
+absorption) spent in state @math{i}, assuming that the state
+occupancy probabilities at time 0 are @var{p}.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item Q
+Infinitesimal generator matrix. @code{@var{Q}(i,j)} is the transition
+rate from state @math{i} to state @math{j},
+ at math{1 @leq{} i \neq j @leq{} N}. The
+matrix @var{Q} must also satisfy the condition @math{\sum_{j=1}^N Q_{ij} = 0}
+
+ at item t
+Time. If omitted, the results are computed until absorption.
+
+ at item p
+ at 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}
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item M
+When called with three arguments, @code{@var{M}(i)} is the expected
+fraction of the interval @math{[0,t]} spent in state @math{i}
+assuming that the state occupancy probability at time zero is
+ at var{p}. When called with two arguments, @code{@var{M}(i)} is the
+expected fraction of time until absorption spent in state @math{i};
+in this case the mean time to absorption is @code{sum(@var{M})}.
+
+ at end table
+
+ at seealso{dtmctaexps}
+
+ at end deftypefn
+
+
+ at noindent @strong{EXAMPLE}
+
+ at example
+ at group
+ at 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-5,30,100);
+ p = zeros(1,N); p(1)=1;
+ M = zeros(length(t),N);
+ for i=1:length(t)
+   M(i,:) = ctmctaexps(Q,t(i),p);
+ endfor
+ clf;
+ 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");
+ at end verbatim
+ at end group
+ at end example
+
+ at c
+ at c
+ at c
+ at node Mean time to absorption (CTMC)
+ at subsection Mean Time to Absorption
+
+ at anchor{doc-ctmcmtta}
+
+
+ at deftypefn {Function File} {@var{t} =} ctmcmtta (@var{Q}, @var{p})
+
+ at cindex Markov chain, continuous time
+ at cindex continuous time Markov chain
+ at cindex CTMC
+ at cindex mean time to absorption, CTMC
+
+Compute the Mean-Time to Absorption (MTTA) of the CTMC described by
+the infinitesimal generator matrix @var{Q}, starting from initial
+occupancy probabilities @var{p}. If there are no absorbing states, this
+function fails with an error.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item Q
+ at 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
+ at math{\sum_{j=1}^N Q_{i j} = 0}
+
+ at item p
+ at 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}
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item t
+Mean time to absorption of the process represented by matrix @var{Q}.
+If there are no absorbing states, this function fails.
+
+ at end table
+
+ at seealso{dtmcmtta}
+
+ at end deftypefn
+
+
+ at 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
+ at math{\{ 2, 3, 4, 5 \}}. State @math{i} denotes the fact that exactly
+ at 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
+ at 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:
+
+ at example
+ at group
+ at verbatim
+ mu = 0.01;
+ death = [ 3 4 5 ] * mu;
+ birth = 0*death;
+ Q = ctmcbd(birth,death);
+ t = ctmcmtta(Q,[0 0 0 1])
+ at end verbatim
+    @result{} t = 78.333
+ at end group
+ at end example
+
+ at 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.
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at c
+ at c
+ at c
+ at node First passage times (CTMC)
+ at subsection First Passage Times
+
+ at anchor{doc-ctmcfpt}
+
+
+ at deftypefn {Function File} {@var{M} =} ctmcfpt (@var{Q})
+ at deftypefnx {Function File} {@var{m} =} ctmcfpt (@var{Q}, @var{i}, @var{j})
+
+ at cindex first passage times, CTMC
+ at cindex CTMC
+ at cindex continuous time Markov chain
+ at cindex Markov chain, continuous time
+
+Compute mean first passage times for an irreducible continuous-time
+Markov chain.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at 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
+ at 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}
+
+ at item i
+Initial state.
+
+ at item j
+Destination state.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item M
+ at code{@var{M}(i,j)} is the average time before state
+ at var{j} is visited for the first time, starting from state @var{i}.
+We set @code{@var{M}(i,i) = 0}.
+
+ at item m
+ at var{m} is the average time before state @var{j} is visited for the first 
+time, starting from state @var{i}.
+
+ at end table
+
+ at seealso{dtmcfpt}
+
+ at end deftypefn
+
+
diff --git a/doc/power.eps b/doc/power.eps
new file mode 100644
index 0000000..9afe743
--- /dev/null
+++ b/doc/power.eps
@@ -0,0 +1,809 @@
+%!PS-Adobe-2.0 EPSF-2.0
+%%BoundingBox: 55 61 262 167
+%%HiResBoundingBox: 55.662 61.674 261.470 166.812
+%%Title: /tmp/oct-52gXuO.eps
+%%Creator: gnuplot 4.6 patchlevel 0
+%%CreationDate: Mon Mar 10 11:37:28 2014
+%%DocumentFonts: (atend)
+%%EndComments
+%%BeginProlog
+/gnudict 256 dict def
+gnudict begin
+%
+% The following true/false flags may be edited by hand if desired.
+% The unit line width and grayscale image gamma correction may also be changed.
+%
+/Color false def
+/Blacktext false def
+/Solid false def
+/Dashlength 1 def
+/Landscape false def
+/Level1 false def
+/Rounded false def
+/ClipToBoundingBox false def
+/SuppressPDFMark false def
+/TransparentPatterns false def
+/gnulinewidth 5.000 def
+/userlinewidth gnulinewidth def
+/Gamma 1.0 def
+/BackgroundColor {-1.000 -1.000 -1.000} def
+%
+/vshift -66 def
+/dl1 {
+  10.0 Dashlength mul mul
+  Rounded { currentlinewidth 0.75 mul sub dup 0 le { pop 0.01 } if } if
+} def
+/dl2 {
+  10.0 Dashlength mul mul
+  Rounded { currentlinewidth 0.75 mul add } if
+} def
+/hpt_ 31.5 def
+/vpt_ 31.5 def
+/hpt hpt_ def
+/vpt vpt_ def
+/doclip {
+  ClipToBoundingBox {
+    newpath 50 50 moveto 251 50 lineto 251 176 lineto 50 176 lineto closepath
+    clip
+  } if
+} def
+%
+% Gnuplot Prolog Version 4.4 (August 2010)
+%
+%/SuppressPDFMark true def
+%
+/M {moveto} bind def
+/L {lineto} bind def
+/R {rmoveto} bind def
+/V {rlineto} bind def
+/N {newpath moveto} bind def
+/Z {closepath} bind def
+/C {setrgbcolor} bind def
+/f {rlineto fill} bind def
+/g {setgray} bind def
+/Gshow {show} def   % May be redefined later in the file to support UTF-8
+/vpt2 vpt 2 mul def
+/hpt2 hpt 2 mul def
+/Lshow {currentpoint stroke M 0 vshift R 
+	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
+/Rshow {currentpoint stroke M dup stringwidth pop neg vshift R
+	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
+/Cshow {currentpoint stroke M dup stringwidth pop -2 div vshift R 
+	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
+/UP {dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def
+  /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def} def
+/DL {Color {setrgbcolor Solid {pop []} if 0 setdash}
+ {pop pop pop 0 setgray Solid {pop []} if 0 setdash} ifelse} def
+/BL {stroke userlinewidth 2 mul setlinewidth
+	Rounded {1 setlinejoin 1 setlinecap} if} def
+/AL {stroke userlinewidth 2 div setlinewidth
+	Rounded {1 setlinejoin 1 setlinecap} if} def
+/UL {dup gnulinewidth mul /userlinewidth exch def
+	dup 1 lt {pop 1} if 10 mul /udl exch def} def
+/PL {stroke userlinewidth setlinewidth
+	Rounded {1 setlinejoin 1 setlinecap} if} def
+3.8 setmiterlimit
+% Default Line colors
+/LCw {1 1 1} def
+/LCb {0 0 0} def
+/LCa {0 0 0} def
+/LC0 {1 0 0} def
+/LC1 {0 1 0} def
+/LC2 {0 0 1} def
+/LC3 {1 0 1} def
+/LC4 {0 1 1} def
+/LC5 {1 1 0} def
+/LC6 {0 0 0} def
+/LC7 {1 0.3 0} def
+/LC8 {0.5 0.5 0.5} def
+% Default Line Types
+/LTw {PL [] 1 setgray} def
+/LTb {BL [] LCb DL} def
+/LTa {AL [1 udl mul 2 udl mul] 0 setdash LCa setrgbcolor} def
+/LT0 {PL [] LC0 DL} def
+/LT1 {PL [4 dl1 2 dl2] LC1 DL} def
+/LT2 {PL [2 dl1 3 dl2] LC2 DL} def
+/LT3 {PL [1 dl1 1.5 dl2] LC3 DL} def
+/LT4 {PL [6 dl1 2 dl2 1 dl1 2 dl2] LC4 DL} def
+/LT5 {PL [3 dl1 3 dl2 1 dl1 3 dl2] LC5 DL} def
+/LT6 {PL [2 dl1 2 dl2 2 dl1 6 dl2] LC6 DL} def
+/LT7 {PL [1 dl1 2 dl2 6 dl1 2 dl2 1 dl1 2 dl2] LC7 DL} def
+/LT8 {PL [2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 4 dl2] LC8 DL} def
+/Pnt {stroke [] 0 setdash gsave 1 setlinecap M 0 0 V stroke grestore} def
+/Dia {stroke [] 0 setdash 2 copy vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V closepath stroke
+  Pnt} def
+/Pls {stroke [] 0 setdash vpt sub M 0 vpt2 V
+  currentpoint stroke M
+  hpt neg vpt neg R hpt2 0 V stroke
+ } def
+/Box {stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V closepath stroke
+  Pnt} def
+/Crs {stroke [] 0 setdash exch hpt sub exch vpt add M
+  hpt2 vpt2 neg V currentpoint stroke M
+  hpt2 neg 0 R hpt2 vpt2 V stroke} def
+/TriU {stroke [] 0 setdash 2 copy vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V closepath stroke
+  Pnt} def
+/Star {2 copy Pls Crs} def
+/BoxF {stroke [] 0 setdash exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V closepath fill} def
+/TriUF {stroke [] 0 setdash vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V closepath fill} def
+/TriD {stroke [] 0 setdash 2 copy vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V closepath stroke
+  Pnt} def
+/TriDF {stroke [] 0 setdash vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V closepath fill} def
+/DiaF {stroke [] 0 setdash vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V closepath fill} def
+/Pent {stroke [] 0 setdash 2 copy gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  closepath stroke grestore Pnt} def
+/PentF {stroke [] 0 setdash gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  closepath fill grestore} def
+/Circle {stroke [] 0 setdash 2 copy
+  hpt 0 360 arc stroke Pnt} def
+/CircleF {stroke [] 0 setdash hpt 0 360 arc fill} def
+/C0 {BL [] 0 setdash 2 copy moveto vpt 90 450 arc} bind def
+/C1 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 90 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C2 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 90 180 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C3 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 180 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C4 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 180 270 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C5 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 90 arc
+	2 copy moveto
+	2 copy vpt 180 270 arc closepath fill
+	vpt 0 360 arc} bind def
+/C6 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 90 270 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C7 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 270 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C8 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 270 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C9 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 270 450 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C10 {BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill
+	2 copy moveto
+	2 copy vpt 90 180 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C11 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 180 arc closepath fill
+	2 copy moveto
+	2 copy vpt 270 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C12 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 180 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C13 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 90 arc closepath fill
+	2 copy moveto
+	2 copy vpt 180 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C14 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 90 360 arc closepath fill
+	vpt 0 360 arc} bind def
+/C15 {BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/Rec {newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto
+	neg 0 rlineto closepath} bind def
+/Square {dup Rec} bind def
+/Bsquare {vpt sub exch vpt sub exch vpt2 Square} bind def
+/S0 {BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare} bind def
+/S1 {BL [] 0 setdash 2 copy vpt Square fill Bsquare} bind def
+/S2 {BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def
+/S3 {BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare} bind def
+/S4 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def
+/S5 {BL [] 0 setdash 2 copy 2 copy vpt Square fill
+	exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def
+/S6 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare} bind def
+/S7 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill
+	2 copy vpt Square fill Bsquare} bind def
+/S8 {BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare} bind def
+/S9 {BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare} bind def
+/S10 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill
+	Bsquare} bind def
+/S11 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill
+	Bsquare} bind def
+/S12 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare} bind def
+/S13 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
+	2 copy vpt Square fill Bsquare} bind def
+/S14 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
+	2 copy exch vpt sub exch vpt Square fill Bsquare} bind def
+/S15 {BL [] 0 setdash 2 copy Bsquare fill Bsquare} bind def
+/D0 {gsave translate 45 rotate 0 0 S0 stroke grestore} bind def
+/D1 {gsave translate 45 rotate 0 0 S1 stroke grestore} bind def
+/D2 {gsave translate 45 rotate 0 0 S2 stroke grestore} bind def
+/D3 {gsave translate 45 rotate 0 0 S3 stroke grestore} bind def
+/D4 {gsave translate 45 rotate 0 0 S4 stroke grestore} bind def
+/D5 {gsave translate 45 rotate 0 0 S5 stroke grestore} bind def
+/D6 {gsave translate 45 rotate 0 0 S6 stroke grestore} bind def
+/D7 {gsave translate 45 rotate 0 0 S7 stroke grestore} bind def
+/D8 {gsave translate 45 rotate 0 0 S8 stroke grestore} bind def
+/D9 {gsave translate 45 rotate 0 0 S9 stroke grestore} bind def
+/D10 {gsave translate 45 rotate 0 0 S10 stroke grestore} bind def
+/D11 {gsave translate 45 rotate 0 0 S11 stroke grestore} bind def
+/D12 {gsave translate 45 rotate 0 0 S12 stroke grestore} bind def
+/D13 {gsave translate 45 rotate 0 0 S13 stroke grestore} bind def
+/D14 {gsave translate 45 rotate 0 0 S14 stroke grestore} bind def
+/D15 {gsave translate 45 rotate 0 0 S15 stroke grestore} bind def
+/DiaE {stroke [] 0 setdash vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V closepath stroke} def
+/BoxE {stroke [] 0 setdash exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V closepath stroke} def
+/TriUE {stroke [] 0 setdash vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V closepath stroke} def
+/TriDE {stroke [] 0 setdash vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V closepath stroke} def
+/PentE {stroke [] 0 setdash gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  closepath stroke grestore} def
+/CircE {stroke [] 0 setdash 
+  hpt 0 360 arc stroke} def
+/Opaque {gsave closepath 1 setgray fill grestore 0 setgray closepath} def
+/DiaW {stroke [] 0 setdash vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V Opaque stroke} def
+/BoxW {stroke [] 0 setdash exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V Opaque stroke} def
+/TriUW {stroke [] 0 setdash vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V Opaque stroke} def
+/TriDW {stroke [] 0 setdash vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V Opaque stroke} def
+/PentW {stroke [] 0 setdash gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  Opaque stroke grestore} def
+/CircW {stroke [] 0 setdash 
+  hpt 0 360 arc Opaque stroke} def
+/BoxFill {gsave Rec 1 setgray fill grestore} def
+/Density {
+  /Fillden exch def
+  currentrgbcolor
+  /ColB exch def /ColG exch def /ColR exch def
+  /ColR ColR Fillden mul Fillden sub 1 add def
+  /ColG ColG Fillden mul Fillden sub 1 add def
+  /ColB ColB Fillden mul Fillden sub 1 add def
+  ColR ColG ColB setrgbcolor} def
+/BoxColFill {gsave Rec PolyFill} def
+/PolyFill {gsave Density fill grestore grestore} def
+/h {rlineto rlineto rlineto gsave closepath fill grestore} bind def
+%
+% PostScript Level 1 Pattern Fill routine for rectangles
+% Usage: x y w h s a XX PatternFill
+%	x,y = lower left corner of box to be filled
+%	w,h = width and height of box
+%	  a = angle in degrees between lines and x-axis
+%	 XX = 0/1 for no/yes cross-hatch
+%
+/PatternFill {gsave /PFa [ 9 2 roll ] def
+  PFa 0 get PFa 2 get 2 div add PFa 1 get PFa 3 get 2 div add translate
+  PFa 2 get -2 div PFa 3 get -2 div PFa 2 get PFa 3 get Rec
+  gsave 1 setgray fill grestore clip
+  currentlinewidth 0.5 mul setlinewidth
+  /PFs PFa 2 get dup mul PFa 3 get dup mul add sqrt def
+  0 0 M PFa 5 get rotate PFs -2 div dup translate
+  0 1 PFs PFa 4 get div 1 add floor cvi
+	{PFa 4 get mul 0 M 0 PFs V} for
+  0 PFa 6 get ne {
+	0 1 PFs PFa 4 get div 1 add floor cvi
+	{PFa 4 get mul 0 2 1 roll M PFs 0 V} for
+ } if
+  stroke grestore} def
+%
+/languagelevel where
+ {pop languagelevel} {1} ifelse
+ 2 lt
+	{/InterpretLevel1 true def}
+	{/InterpretLevel1 Level1 def}
+ ifelse
+%
+% PostScript level 2 pattern fill definitions
+%
+/Level2PatternFill {
+/Tile8x8 {/PaintType 2 /PatternType 1 /TilingType 1 /BBox [0 0 8 8] /XStep 8 /YStep 8}
+	bind def
+/KeepColor {currentrgbcolor [/Pattern /DeviceRGB] setcolorspace} bind def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke} 
+>> matrix makepattern
+/Pat1 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke
+	0 4 M 4 8 L 8 4 L 4 0 L 0 4 L stroke}
+>> matrix makepattern
+/Pat2 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 0 0 M 0 8 L
+	8 8 L 8 0 L 0 0 L fill}
+>> matrix makepattern
+/Pat3 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop -4 8 M 8 -4 L
+	0 12 M 12 0 L stroke}
+>> matrix makepattern
+/Pat4 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop -4 0 M 8 12 L
+	0 -4 M 12 8 L stroke}
+>> matrix makepattern
+/Pat5 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop -2 8 M 4 -4 L
+	0 12 M 8 -4 L 4 12 M 10 0 L stroke}
+>> matrix makepattern
+/Pat6 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop -2 0 M 4 12 L
+	0 -4 M 8 12 L 4 -4 M 10 8 L stroke}
+>> matrix makepattern
+/Pat7 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 8 -2 M -4 4 L
+	12 0 M -4 8 L 12 4 M 0 10 L stroke}
+>> matrix makepattern
+/Pat8 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 0 -2 M 12 4 L
+	-4 0 M 12 8 L -4 4 M 8 10 L stroke}
+>> matrix makepattern
+/Pat9 exch def
+/Pattern1 {PatternBgnd KeepColor Pat1 setpattern} bind def
+/Pattern2 {PatternBgnd KeepColor Pat2 setpattern} bind def
+/Pattern3 {PatternBgnd KeepColor Pat3 setpattern} bind def
+/Pattern4 {PatternBgnd KeepColor Landscape {Pat5} {Pat4} ifelse setpattern} bind def
+/Pattern5 {PatternBgnd KeepColor Landscape {Pat4} {Pat5} ifelse setpattern} bind def
+/Pattern6 {PatternBgnd KeepColor Landscape {Pat9} {Pat6} ifelse setpattern} bind def
+/Pattern7 {PatternBgnd KeepColor Landscape {Pat8} {Pat7} ifelse setpattern} bind def
+} def
+%
+%
+%End of PostScript Level 2 code
+%
+/PatternBgnd {
+  TransparentPatterns {} {gsave 1 setgray fill grestore} ifelse
+} def
+%
+% Substitute for Level 2 pattern fill codes with
+% grayscale if Level 2 support is not selected.
+%
+/Level1PatternFill {
+/Pattern1 {0.250 Density} bind def
+/Pattern2 {0.500 Density} bind def
+/Pattern3 {0.750 Density} bind def
+/Pattern4 {0.125 Density} bind def
+/Pattern5 {0.375 Density} bind def
+/Pattern6 {0.625 Density} bind def
+/Pattern7 {0.875 Density} bind def
+} def
+%
+% Now test for support of Level 2 code
+%
+Level1 {Level1PatternFill} {Level2PatternFill} ifelse
+%
+/Symbol-Oblique /Symbol findfont [1 0 .167 1 0 0] makefont
+dup length dict begin {1 index /FID eq {pop pop} {def} ifelse} forall
+currentdict end definefont pop
+/MFshow {
+   { dup 5 get 3 ge
+     { 5 get 3 eq {gsave} {grestore} ifelse }
+     {dup dup 0 get findfont exch 1 get scalefont setfont
+     [ currentpoint ] exch dup 2 get 0 exch R dup 5 get 2 ne {dup dup 6
+     get exch 4 get {Gshow} {stringwidth pop 0 R} ifelse }if dup 5 get 0 eq
+     {dup 3 get {2 get neg 0 exch R pop} {pop aload pop M} ifelse} {dup 5
+     get 1 eq {dup 2 get exch dup 3 get exch 6 get stringwidth pop -2 div
+     dup 0 R} {dup 6 get stringwidth pop -2 div 0 R 6 get
+     show 2 index {aload pop M neg 3 -1 roll neg R pop pop} {pop pop pop
+     pop aload pop M} ifelse }ifelse }ifelse }
+     ifelse }
+   forall} def
+/Gswidth {dup type /stringtype eq {stringwidth} {pop (n) stringwidth} ifelse} def
+/MFwidth {0 exch { dup 5 get 3 ge { 5 get 3 eq { 0 } { pop } ifelse }
+ {dup 3 get{dup dup 0 get findfont exch 1 get scalefont setfont
+     6 get Gswidth pop add} {pop} ifelse} ifelse} forall} def
+/MLshow { currentpoint stroke M
+  0 exch R
+  Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def
+/MRshow { currentpoint stroke M
+  exch dup MFwidth neg 3 -1 roll R
+  Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def
+/MCshow { currentpoint stroke M
+  exch dup MFwidth -2 div 3 -1 roll R
+  Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def
+/XYsave    { [( ) 1 2 true false 3 ()] } bind def
+/XYrestore { [( ) 1 2 true false 4 ()] } bind def
+Level1 SuppressPDFMark or 
+{} {
+/SDict 10 dict def
+systemdict /pdfmark known not {
+  userdict /pdfmark systemdict /cleartomark get put
+} if
+SDict begin [
+  /Title (/tmp/oct-52gXuO.eps)
+  /Subject (gnuplot plot)
+  /Creator (gnuplot 4.6 patchlevel 0)
+  /Author (marzolla)
+%  /Producer (gnuplot)
+%  /Keywords ()
+  /CreationDate (Mon Mar 10 11:37:28 2014)
+  /DOCINFO pdfmark
+end
+} ifelse
+end
+%%EndProlog
+%%Page: 1 1
+gnudict begin
+gsave
+doclip
+50 50 translate
+0.050 0.050 scale
+0 setgray
+newpath
+(Helvetica) findfont 200 scalefont setfont
+BackgroundColor 0 lt 3 1 roll 0 lt exch 0 lt or or not {BackgroundColor C 1.000 0 0 4030.00 2520.00 BoxColFill} if
+gsave % colour palette begin
+/maxcolors 64 def
+/HSV2RGB {  exch dup 0.0 eq {pop exch pop dup dup} % achromatic gray
+  { /HSVs exch def /HSVv exch def 6.0 mul dup floor dup 3 1 roll sub
+     /HSVf exch def /HSVi exch cvi def /HSVp HSVv 1.0 HSVs sub mul def
+	 /HSVq HSVv 1.0 HSVs HSVf mul sub mul def 
+	 /HSVt HSVv 1.0 HSVs 1.0 HSVf sub mul sub mul def
+	 /HSVi HSVi 6 mod def 0 HSVi eq {HSVv HSVt HSVp}
+	 {1 HSVi eq {HSVq HSVv HSVp}{2 HSVi eq {HSVp HSVv HSVt}
+	 {3 HSVi eq {HSVp HSVq HSVv}{4 HSVi eq {HSVt HSVp HSVv}
+	 {HSVv HSVp HSVq} ifelse} ifelse} ifelse} ifelse} ifelse
+  } ifelse} def
+/Constrain {
+  dup 0 lt {0 exch pop}{dup 1 gt {1 exch pop} if} ifelse} def
+/YIQ2RGB {
+  3 copy -1.702 mul exch -1.105 mul add add Constrain 4 1 roll
+  3 copy -0.647 mul exch -0.272 mul add add Constrain 5 1 roll
+  0.621 mul exch -0.956 mul add add Constrain 3 1 roll } def
+/CMY2RGB {  1 exch sub exch 1 exch sub 3 2 roll 1 exch sub 3 1 roll exch } def
+/XYZ2RGB {  3 copy -0.9017 mul exch -0.1187 mul add exch 0.0585 mul exch add
+  Constrain 4 1 roll 3 copy -0.0279 mul exch 1.999 mul add exch
+  -0.9844 mul add Constrain 5 1 roll -0.2891 mul exch -0.5338 mul add
+  exch 1.91 mul exch add Constrain 3 1 roll} def
+/SelectSpace {ColorSpace (HSV) eq {HSV2RGB}{ColorSpace (XYZ) eq {
+  XYZ2RGB}{ColorSpace (CMY) eq {CMY2RGB}{ColorSpace (YIQ) eq {YIQ2RGB}
+  if} ifelse} ifelse} ifelse} def
+/InterpolatedColor true def
+/grayindex {/gidx 0 def
+  {GrayA gidx get grayv ge {exit} if /gidx gidx 1 add def} loop} def
+/dgdx {grayv GrayA gidx get sub GrayA gidx 1 sub get
+  GrayA gidx get sub div} def 
+/redvalue {RedA gidx get RedA gidx 1 sub get
+  RedA gidx get sub dgdxval mul add} def
+/greenvalue {GreenA gidx get GreenA gidx 1 sub get
+  GreenA gidx get sub dgdxval mul add} def
+/bluevalue {BlueA gidx get BlueA gidx 1 sub get
+  BlueA gidx get sub dgdxval mul add} def
+/interpolate {
+  grayindex grayv GrayA gidx get sub abs 1e-5 le
+    {RedA gidx get GreenA gidx get BlueA gidx get}
+    {/dgdxval dgdx def redvalue greenvalue bluevalue} ifelse} def
+/GrayA [0 .0159 .0317 .0476 .0635 .0794 .0952 .1111 .127 .1429 .1587 .1746 
+  .1905 .2063 .2222 .2381 .254 .2698 .2857 .3016 .3175 .3333 .3492 .3651 
+  .381 .3968 .4127 .4286 .4444 .4603 .4762 .4921 .5079 .5238 .5397 .5556 
+  .5714 .5873 .6032 .619 .6349 .6508 .6667 .6825 .6984 .7143 .7302 .746 
+  .7619 .7778 .7937 .8095 .8254 .8413 .8571 .873 .8889 .9048 .9206 .9365 
+  .9524 .9683 .9841 1 ] def
+/RedA [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .0238 .0873 .1508 
+  .2143 .2778 .3413 .4048 .4683 .5317 .5952 .6587 .7222 .7857 .8492 .9127 
+  .9762 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 .9444 .881 .8175 .754 .6905 .627 
+  .5635 .5 ] def
+/GreenA [0 0 0 0 0 0 0 0 .0079 .0714 .1349 .1984 .2619 .3254 .3889 .4524 
+  .5159 .5794 .6429 .7063 .7698 .8333 .8968 .9603 1 1 1 1 1 1 1 1 1 1 1 1 1 
+  1 1 1 .9603 .8968 .8333 .7698 .7063 .6429 .5794 .5159 .4524 .3889 .3254 
+  .2619 .1984 .1349 .0714 .0079 0 0 0 0 0 0 0 0 ] def
+/BlueA [.5 .5635 .627 .6905 .754 .8175 .881 .9444 1 1 1 1 1 1 1 1 1 1 1 1 1 
+  1 1 1 .9762 .9127 .8492 .7857 .7222 .6587 .5952 .5317 .4683 .4048 .3413 
+  .2778 .2143 .1508 .0873 .0238 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+  0 0 ] def
+/pm3dround {maxcolors 0 gt {dup 1 ge
+	{pop 1} {maxcolors mul floor maxcolors 1 sub div} ifelse} if} def
+/pm3dGamma 1.0 1.5 Gamma mul div def
+/ColorSpace (RGB) def
+Color InterpolatedColor or { % COLOUR vs. GRAY map
+  InterpolatedColor { %% Interpolation vs. RGB-Formula
+    /g {stroke pm3dround /grayv exch def interpolate
+        SelectSpace setrgbcolor} bind def
+  }{
+  /g {stroke pm3dround dup cF7 Constrain exch dup cF5 Constrain exch cF15 Constrain 
+       SelectSpace setrgbcolor} bind def
+  } ifelse
+}{
+  /g {stroke pm3dround pm3dGamma exp setgray} bind def
+} ifelse
+0.500 UL
+LTb
+1340 840 M
+88 0 V
+2241 0 R
+-88 0 V
+stroke
+1220 840 M
+[ [({}) 200.0 0.0 true true 0 (5e-07)]
+] -66.7 MRshow
+0.500 UL
+LTb
+1340 1080 M
+88 0 V
+2241 0 R
+-88 0 V
+stroke
+1220 1080 M
+[ [({}) 200.0 0.0 true true 0 (1e-06)]
+] -66.7 MRshow
+0.500 UL
+LTb
+1340 1320 M
+88 0 V
+2241 0 R
+-88 0 V
+stroke
+1220 1320 M
+[ [({}) 200.0 0.0 true true 0 (1.5e-06)]
+] -66.7 MRshow
+0.500 UL
+LTb
+1340 1559 M
+88 0 V
+2241 0 R
+-88 0 V
+stroke
+1220 1559 M
+[ [({}) 200.0 0.0 true true 0 (2e-06)]
+] -66.7 MRshow
+0.500 UL
+LTb
+1340 1799 M
+88 0 V
+2241 0 R
+-88 0 V
+stroke
+1220 1799 M
+[ [({}) 200.0 0.0 true true 0 (2.5e-06)]
+] -66.7 MRshow
+0.500 UL
+LTb
+1340 2039 M
+88 0 V
+2241 0 R
+-88 0 V
+stroke
+1220 2039 M
+[ [({}) 200.0 0.0 true true 0 (3e-06)]
+] -66.7 MRshow
+0.500 UL
+LTb
+1340 2279 M
+88 0 V
+2241 0 R
+-88 0 V
+stroke
+1220 2279 M
+[ [({}) 200.0 0.0 true true 0 (3.5e-06)]
+] -66.7 MRshow
+0.500 UL
+LTb
+1340 840 M
+0 88 V
+0 1351 R
+0 -88 V
+stroke
+1340 640 M
+[ [({}) 200.0 0.0 true true 0 (0)]
+] -66.7 MCshow
+0.500 UL
+LTb
+1806 840 M
+0 88 V
+0 1351 R
+0 -88 V
+stroke
+1806 640 M
+[ [({}) 200.0 0.0 true true 0 (0.2)]
+] -66.7 MCshow
+0.500 UL
+LTb
+2272 840 M
+0 88 V
+0 1351 R
+0 -88 V
+stroke
+2272 640 M
+[ [({}) 200.0 0.0 true true 0 (0.4)]
+] -66.7 MCshow
+0.500 UL
+LTb
+2737 840 M
+0 88 V
+0 1351 R
+0 -88 V
+stroke
+2737 640 M
+[ [({}) 200.0 0.0 true true 0 (0.6)]
+] -66.7 MCshow
+0.500 UL
+LTb
+3203 840 M
+0 88 V
+0 1351 R
+0 -88 V
+stroke
+3203 640 M
+[ [({}) 200.0 0.0 true true 0 (0.8)]
+] -66.7 MCshow
+0.500 UL
+LTb
+3669 840 M
+0 88 V
+0 1351 R
+0 -88 V
+stroke
+3669 640 M
+[ [({}) 200.0 0.0 true true 0 (1)]
+] -66.7 MCshow
+0.500 UL
+LTb
+0.500 UL
+LTb
+1340 2279 N
+0 -1439 V
+2329 0 V
+0 1439 V
+-2329 0 V
+Z stroke
+LCb setrgbcolor
+160 1559 M
+currentpoint gsave translate 90 rotate 0 0 moveto
+[ [({}) 200.0 0.0 true true 0 (Power)]
+] -66.7 MCshow
+grestore
+LTb
+LCb setrgbcolor
+2504 340 M
+[ [({}) 200.0 0.0 true true 0 (Population mix )]
+[(Symbol) 200.0 0.0 true true 0 (b)]
+[({}) 160.0 -60.0 true true 0 (1)]
+[({}) 200.0 0.0 true true 0 ( for Class 1)]
+] -46.7 MCshow
+LTb
+1.000 UP
+0.500 UL
+LTb
+% Begin plot #1
+2.000 UL
+LT1
+LCb setrgbcolor
+2766 2116 M
+[ [({}) 200.0 0.0 true true 0 (Class 1)]
+] -66.7 MRshow
+LT1
+2886 2116 M
+543 0 V
+1573 1538 M
+110 307 V
+109 255 V
+110 24 V
+109 -128 V
+110 -195 V
+110 -158 V
+109 -99 V
+110 -100 V
+109 -81 V
+110 -67 V
+110 -44 V
+109 -39 V
+110 -13 V
+109 6 V
+110 20 V
+109 28 V
+110 30 V
+% End plot #1
+% Begin plot #2
+stroke
+LT2
+LCb setrgbcolor
+2766 1916 M
+[ [({}) 200.0 0.0 true true 0 (Class 2)]
+] -66.7 MRshow
+LT2
+2886 1916 M
+543 0 V
+1573 1147 M
+110 -14 V
+109 -10 V
+110 8 V
+109 21 V
+110 40 V
+110 48 V
+109 44 V
+110 64 V
+109 78 V
+110 96 V
+110 88 V
+109 95 V
+110 10 V
+109 -72 V
+110 -157 V
+109 -198 V
+110 -220 V
+% End plot #2
+% Begin plot #3
+stroke
+LT0
+LCb setrgbcolor
+2766 1716 M
+[ [({}) 200.0 0.0 true true 0 (System)]
+] -66.7 MRshow
+LT0
+2886 1716 M
+543 0 V
+1573 1616 M
+110 182 V
+109 206 V
+110 133 V
+109 40 V
+110 8 V
+110 1 V
+109 0 V
+110 0 V
+109 0 V
+110 0 V
+110 -3 V
+109 -23 V
+110 -69 V
+109 -87 V
+110 -130 V
+109 -137 V
+110 -135 V
+% End plot #3
+1.000 UP
+stroke
+0.500 UL
+LTb
+grestore % colour palette end
+stroke
+grestore
+end
+showpage
+%%Trailer
+%%DocumentFonts: Symbol Helvetica
diff --git a/doc/qn_closed_multi.eps b/doc/qn_closed_multi.eps
new file mode 100644
index 0000000..bad7b69
--- /dev/null
+++ b/doc/qn_closed_multi.eps
@@ -0,0 +1,209 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Title: qn_closed_multi.fig
+%%Creator: fig2dev Version 3.2 Patchlevel 5d
+%%CreationDate: Mon Mar 10 11:38:03 2014
+%%BoundingBox: 0 0 172 161
+%Magnification: 1.0000
+%%EndComments
+%%BeginProlog
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+  bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+  4 -2 roll mul srgb} bind def
+ /DrawEllipse {
+	/endangle exch def
+	/startangle exch def
+	/yrad exch def
+	/xrad exch def
+	/y exch def
+	/x exch def
+	/savematrix mtrx currentmatrix def
+	x y tr xrad yrad sc 0 0 1 startangle endangle arc
+	closepath
+	savematrix setmatrix
+	} def
+
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+/pageheader {
+save
+newpath 0 161 moveto 0 0 lineto 172 0 lineto 172 161 lineto closepath clip newpath
+-299.7 397.8 translate
+1 -1 scale
+$F2psBegin
+10 setmiterlimit
+0 slj 0 slc
+ 0.06299 0.06299 sc
+} bind def
+/pagefooter {
+$F2psEnd
+restore
+} bind def
+%%EndProlog
+pageheader
+%
+% Fig objects follow
+%
+% 
+% here starts figure with depth 50
+% Ellipse
+7.500 slw
+n 5310 5040 270 270 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 6930 5580 270 270 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 6930 4500 270 270 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+0 slj
+0 slc
+n 6300 5310 m 6660 5310 l 6660 5850 l
+ 6300 5850 l gs col0 s gr 
+% Polyline
+n 6300 4230 m 6660 4230 l 6660 4770 l
+ 6300 4770 l gs col0 s gr 
+% Polyline
+gs  clippath
+6163 4530 m 6315 4530 l 6315 4470 l 6163 4470 l 6163 4470 l 6283 4500 l 6163 4530 l cp
+eoclip
+n 5580 4950 m 5850 4950 l 6030 4500 l
+ 6300 4500 l gs col0 s gr gr
+
+% arrowhead
+n 6163 4530 m 6283 4500 l 6163 4470 l  col0 s
+% Polyline
+gs  clippath
+4903 4980 m 5055 4980 l 5055 4920 l 4903 4920 l 4903 4920 l 5023 4950 l 4903 4980 l cp
+eoclip
+n 7200 4500 m 7470 4500 l 7470 3960 l 4770 3960 l 4770 4950 l
+ 5040 4950 l gs col0 s gr gr
+
+% arrowhead
+n 4903 4980 m 5023 4950 l 4903 4920 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+6118 5610 m 6270 5610 l 6270 5550 l 6118 5550 l 6118 5550 l 6238 5580 l 6118 5610 l cp
+eoclip
+n 5580 5130 m 5850 5130 l 6030 5580 l
+ 6255 5580 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 6118 5610 m 6238 5580 l 6118 5550 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+4903 5160 m 5055 5160 l 5055 5100 l 4903 5100 l 4903 5100 l 5023 5130 l 4903 5160 l cp
+eoclip
+n 7200 5580 m 7470 5580 l 7470 6120 l 4770 6120 l 4770 5130 l
+ 5040 5130 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4903 5160 m 5023 5130 l 4903 5100 l  col0 s
+/Times-Roman ff 180.00 scf sf
+5130 5490 m
+gs 1 -1 sc (CPU) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 6030 m
+gs 1 -1 sc (Disk2) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5220 5085 m
+gs 1 -1 sc (S) col0 sh gr
+/Times-Roman ff 135.00 scf sf
+5325 5141 m
+gs 1 -1 sc (1) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6840 5625 m
+gs 1 -1 sc (S) col0 sh gr
+/Times-Roman ff 135.00 scf sf
+6945 5681 m
+gs 1 -1 sc (3) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 4950 m
+gs 1 -1 sc (Disk1) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6840 4545 m
+gs 1 -1 sc (S) col0 sh gr
+/Times-Roman ff 135.00 scf sf
+6945 4601 m
+gs 1 -1 sc (2) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5490 6300 m
+gs 1 -1 sc (Class 2) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5490 3915 m
+gs 1 -1 sc (Class 1) col0 sh gr
+% here ends figure;
+pagefooter
+showpage
+%%Trailer
+%EOF
diff --git a/doc/qn_closed_multi.fig b/doc/qn_closed_multi.fig
new file mode 100644
index 0000000..52906e1
--- /dev/null
+++ b/doc/qn_closed_multi.fig
@@ -0,0 +1,39 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5310 5040 270 270 5310 5040 5310 4770
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6930 5580 270 270 6930 5580 6930 5310
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6930 4500 270 270 6930 4500 6930 4230
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 6300 5310 6660 5310 6660 5850 6300 5850
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 6300 4230 6660 4230 6660 4770 6300 4770
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	0 0 1.00 60.00 120.00
+	 5580 4950 5850 4950 6030 4500 6300 4500
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 6
+	0 0 1.00 60.00 120.00
+	 7200 4500 7470 4500 7470 3960 4770 3960 4770 4950 5040 4950
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 4
+	0 0 1.00 60.00 120.00
+	 5580 5130 5850 5130 6030 5580 6255 5580
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 6
+	0 0 1.00 60.00 120.00
+	 7200 5580 7470 5580 7470 6120 4770 6120 4770 5130 5040 5130
+4 0 0 50 -1 0 12 0.0000 4 135 375 5130 5490 CPU\001
+4 0 0 50 -1 0 12 0.0000 4 135 480 6705 6030 Disk2\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 5220 5085 S\001
+4 0 0 50 -1 0 9 0.0000 4 105 75 5325 5141 1\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 6840 5625 S\001
+4 0 0 50 -1 0 9 0.0000 4 105 75 6945 5681 3\001
+4 0 0 50 -1 0 12 0.0000 4 135 480 6705 4950 Disk1\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 6840 4545 S\001
+4 0 0 50 -1 0 9 0.0000 4 105 75 6945 4601 2\001
+4 0 0 50 -1 0 12 0.0000 4 135 585 5490 6300 Class 2\001
+4 0 0 50 -1 0 12 0.0000 4 135 585 5490 3915 Class 1\001
diff --git a/doc/qn_closed_multi_apl.eps b/doc/qn_closed_multi_apl.eps
new file mode 100644
index 0000000..1bddce5
--- /dev/null
+++ b/doc/qn_closed_multi_apl.eps
@@ -0,0 +1,197 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Title: qn_closed_multi_apl.fig
+%%Creator: fig2dev Version 3.2 Patchlevel 5d
+%%CreationDate: Mon Mar 10 11:38:03 2014
+%%BoundingBox: 0 0 121 144
+%Magnification: 1.0000
+%%EndComments
+%%BeginProlog
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+  bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+  4 -2 roll mul srgb} bind def
+ /DrawEllipse {
+	/endangle exch def
+	/startangle exch def
+	/yrad exch def
+	/xrad exch def
+	/y exch def
+	/x exch def
+	/savematrix mtrx currentmatrix def
+	x y tr xrad yrad sc 0 0 1 startangle endangle arc
+	closepath
+	savematrix setmatrix
+	} def
+
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+/pageheader {
+save
+newpath 0 144 moveto 0 0 lineto 121 0 lineto 121 144 lineto closepath clip newpath
+-322.4 363.6 translate
+1 -1 scale
+$F2psBegin
+10 setmiterlimit
+0 slj 0 slc
+ 0.06299 0.06299 sc
+} bind def
+/pagefooter {
+$F2psEnd
+restore
+} bind def
+%%EndProlog
+pageheader
+%
+% Fig objects follow
+%
+% 
+% here starts figure with depth 50
+% Ellipse
+7.500 slw
+n 6570 4590 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 5670 5220 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 5680 4053 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+0 slj
+0 slc
+gs  clippath
+6253 4530 m 6405 4530 l 6405 4470 l 6253 4470 l 6253 4470 l 6373 4500 l 6253 4530 l cp
+eoclip
+n 5850 4050 m 6120 4050 l 6120 4500 l
+ 6390 4500 l gs col0 s gr gr
+
+% arrowhead
+n 6253 4530 m 6373 4500 l 6253 4470 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+6253 4710 m 6405 4710 l 6405 4650 l 6253 4650 l 6253 4650 l 6373 4680 l 6253 4710 l cp
+eoclip
+n 5850 5220 m 6120 5220 l 6120 4680 l
+ 6390 4680 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 6253 4710 m 6373 4680 l 6253 4650 l  col0 s
+% Polyline
+gs  clippath
+5353 4080 m 5505 4080 l 5505 4020 l 5353 4020 l 5353 4020 l 5473 4050 l 5353 4080 l cp
+eoclip
+n 6750 4500 m 7020 4500 l 7020 3510 l 5130 3510 l 5130 4050 l
+ 5490 4050 l gs col0 s gr gr
+
+% arrowhead
+n 5353 4080 m 5473 4050 l 5353 4020 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+5353 5250 m 5505 5250 l 5505 5190 l 5353 5190 l 5353 5190 l 5473 5220 l 5353 5250 l cp
+eoclip
+n 6750 4680 m 7020 4680 l 7020 5760 l 5130 5760 l 5130 5220 l
+ 5490 5220 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5353 5250 m 5473 5220 l 5353 5190 l  col0 s
+/Times-Roman ff 150.00 scf sf
+5535 3825 m
+gs 1 -1 sc (APL) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5535 4995 m
+gs 1 -1 sc (IMS) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6390 4365 m
+gs 1 -1 sc (SYS) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5625 4095 m
+gs 1 -1 sc (1) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5625 5265 m
+gs 1 -1 sc (2) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6525 4635 m
+gs 1 -1 sc (3) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6480 4950 m
+gs 1 -1 sc (PS) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5625 4410 m
+gs 1 -1 sc (IS) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5625 5580 m
+gs 1 -1 sc (IS) col0 sh gr
+% here ends figure;
+pagefooter
+showpage
+%%Trailer
+%EOF
diff --git a/doc/qn_closed_multi_apl.fig b/doc/qn_closed_multi_apl.fig
new file mode 100644
index 0000000..470f254
--- /dev/null
+++ b/doc/qn_closed_multi_apl.fig
@@ -0,0 +1,33 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6570 4590 180 180 6570 4590 6750 4590
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5670 5220 180 180 5670 5220 5850 5220
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5680 4053 180 180 5680 4053 5860 4053
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	0 0 1.00 60.00 120.00
+	 5850 4050 6120 4050 6120 4500 6390 4500
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 4
+	0 0 1.00 60.00 120.00
+	 5850 5220 6120 5220 6120 4680 6390 4680
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 6
+	0 0 1.00 60.00 120.00
+	 6750 4500 7020 4500 7020 3510 5130 3510 5130 4050 5490 4050
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 6
+	0 0 1.00 60.00 120.00
+	 6750 4680 7020 4680 7020 5760 5130 5760 5130 5220 5490 5220
+4 0 0 50 -1 -1 10 0.0000 4 105 300 5535 3825 APL\001
+4 0 0 50 -1 -1 10 0.0000 4 105 285 5535 4995 IMS\001
+4 0 0 50 -1 -1 10 0.0000 4 105 270 6390 4365 SYS\001
+4 0 0 50 -1 0 10 0.0000 4 105 75 5625 4095 1\001
+4 0 0 50 -1 0 10 0.0000 4 105 75 5625 5265 2\001
+4 0 0 50 -1 0 10 0.0000 4 105 75 6525 4635 3\001
+4 0 0 50 -1 0 10 0.0000 4 105 165 6480 4950 PS\001
+4 0 0 50 -1 0 10 0.0000 4 105 135 5625 4410 IS\001
+4 0 0 50 -1 0 10 0.0000 4 105 135 5625 5580 IS\001
diff --git a/doc/qn_closed_multi_cs.eps b/doc/qn_closed_multi_cs.eps
new file mode 100644
index 0000000..9c60740
--- /dev/null
+++ b/doc/qn_closed_multi_cs.eps
@@ -0,0 +1,316 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Title: qn_closed_multi_cs.fig
+%%Creator: fig2dev Version 3.2 Patchlevel 5d
+%%CreationDate: Mon Mar 10 11:38:03 2014
+%%BoundingBox: 0 0 201 148
+%Magnification: 1.0000
+%%EndComments
+%%BeginProlog
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+  bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+  4 -2 roll mul srgb} bind def
+ /DrawEllipse {
+	/endangle exch def
+	/startangle exch def
+	/yrad exch def
+	/xrad exch def
+	/y exch def
+	/x exch def
+	/savematrix mtrx currentmatrix def
+	x y tr xrad yrad sc 0 0 1 startangle endangle arc
+	closepath
+	savematrix setmatrix
+	} def
+
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+/pageheader {
+save
+newpath 0 148 moveto 0 0 lineto 201 0 lineto 201 148 lineto closepath clip newpath
+-265.5 397.8 translate
+1 -1 scale
+$F2psBegin
+10 setmiterlimit
+0 slj 0 slc
+ 0.06299 0.06299 sc
+} bind def
+/pagefooter {
+$F2psEnd
+restore
+} bind def
+%%EndProlog
+pageheader
+%
+% Fig objects follow
+%
+% 
+% here starts figure with depth 50
+% Ellipse
+7.500 slw
+n 6570 5580 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 4770 5130 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 6570 4680 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+0 slj
+0 slc
+gs  clippath
+7063 4620 m 7215 4620 l 7215 4560 l 7063 4560 l 7063 4560 l 7183 4590 l 7063 4620 l cp
+eoclip
+n 6750 4590 m
+ 7200 4590 l gs col0 s gr gr
+
+% arrowhead
+n 7063 4620 m 7183 4590 l 7063 4560 l  col0 s
+% Polyline
+gs  clippath
+7063 5520 m 7215 5520 l 7215 5460 l 7063 5460 l 7063 5460 l 7183 5490 l 7063 5520 l cp
+eoclip
+n 6750 5490 m
+ 7200 5490 l gs col0 s gr gr
+
+% arrowhead
+n 7063 5520 m 7183 5490 l 7063 5460 l  col0 s
+% Polyline
+gs  clippath
+4453 5070 m 4605 5070 l 4605 5010 l 4453 5010 l 4453 5010 l 4573 5040 l 4453 5070 l cp
+eoclip
+n 7200 5490 m 7200 4140 l 4230 4140 l 4230 5040 l
+ 4590 5040 l gs col0 s gr gr
+
+% arrowhead
+n 4453 5070 m 4573 5040 l 4453 5010 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+5983 5250 m 6135 5250 l 6135 5190 l 5983 5190 l 5983 5190 l 6103 5220 l 5983 5250 l cp
+eoclip
+n 4950 5220 m
+ 6120 5220 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5983 5250 m 6103 5220 l 5983 5190 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+6253 4800 m 6405 4800 l 6405 4740 l 6253 4740 l 6253 4740 l 6373 4770 l 6253 4800 l cp
+eoclip
+n 6120 5220 m 6120 4770 l
+ 6390 4770 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 6253 4800 m 6373 4770 l 6253 4740 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+6253 5700 m 6405 5700 l 6405 5640 l 6253 5640 l 6253 5640 l 6373 5670 l 6253 5700 l cp
+eoclip
+n 6120 5220 m 6120 5670 l
+ 6390 5670 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 6253 5700 m 6373 5670 l 6253 5640 l  col0 s
+% Polyline
+gs  clippath
+5713 5070 m 5865 5070 l 5865 5010 l 5713 5010 l 5713 5010 l 5833 5040 l 5713 5070 l cp
+eoclip
+n 4950 5040 m
+ 5850 5040 l gs col0 s gr gr
+
+% arrowhead
+n 5713 5070 m 5833 5040 l 5713 5010 l  col0 s
+% Polyline
+gs  clippath
+6253 4620 m 6405 4620 l 6405 4560 l 6253 4560 l 6253 4560 l 6373 4590 l 6253 4620 l cp
+eoclip
+n 5850 5040 m 5850 4590 l
+ 6390 4590 l gs col0 s gr gr
+
+% arrowhead
+n 6253 4620 m 6373 4590 l 6253 4560 l  col0 s
+% Polyline
+gs  clippath
+6253 5520 m 6405 5520 l 6405 5460 l 6253 5460 l 6253 5460 l 6373 5490 l 6253 5520 l cp
+eoclip
+n 5850 5040 m 5850 5490 l
+ 6390 5490 l gs col0 s gr gr
+
+% arrowhead
+n 6253 5520 m 6373 5490 l 6253 5460 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+7243 4800 m 7395 4800 l 7395 4740 l 7243 4740 l 7243 4740 l 7363 4770 l 7243 4800 l cp
+eoclip
+n 6750 4770 m
+ 7380 4770 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 7243 4800 m 7363 4770 l 7243 4740 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+7243 5700 m 7395 5700 l 7395 5640 l 7243 5640 l 7243 5640 l 7363 5670 l 7243 5700 l cp
+eoclip
+n 6750 5670 m
+ 7380 5670 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 7243 5700 m 7363 5670 l 7243 5640 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+4453 5250 m 4605 5250 l 4605 5190 l 4453 5190 l 4453 5190 l 4573 5220 l 4453 5250 l cp
+eoclip
+n 7380 4770 m 7380 6120 l 4230 6120 l 4230 5220 l
+ 4590 5220 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4453 5250 m 4573 5220 l 4453 5190 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+4367 4560 m 4215 4560 l 4215 4620 l 4367 4620 l 4367 4620 l 4247 4590 l 4367 4560 l cp
+eoclip
+n 5310 5220 m 5310 4590 l
+ 4230 4590 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4367 4560 m 4247 4590 l 4367 4620 l  col0 s
+% Polyline
+gs  clippath
+4367 5640 m 4215 5640 l 4215 5700 l 4367 5700 l 4367 5700 l 4247 5670 l 4367 5640 l cp
+eoclip
+n 5130 5040 m 5130 5670 l
+ 4230 5670 l gs col0 s gr gr
+
+% arrowhead
+n 4367 5640 m 4247 5670 l 4367 5700 l  col0 s
+/Times-Roman ff 150.00 scf sf
+6525 5625 m
+gs 1 -1 sc (3) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+4725 5175 m
+gs 1 -1 sc (1) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6525 4725 m
+gs 1 -1 sc (2) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6165 4950 m
+gs 1 -1 sc (.3) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5805 4545 m
+gs 1 -1 sc (.7) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5805 5670 m
+gs 1 -1 sc (.2) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6075 5850 m
+gs 1 -1 sc (.5) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5265 4545 m
+gs 1 -1 sc (.2) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5085 5850 m
+gs 1 -1 sc (.1) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+4230 4095 m
+gs 1 -1 sc (Class 1) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+4230 6300 m
+gs 1 -1 sc (Class 2) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+4590 4905 m
+gs 1 -1 sc (CPU) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6480 5355 m
+gs 1 -1 sc (I/O) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6480 4455 m
+gs 1 -1 sc (I/O) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+4680 5490 m
+gs 1 -1 sc (PS) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6390 4995 m
+gs 1 -1 sc (FCFS) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6390 5895 m
+gs 1 -1 sc (FCFS) col0 sh gr
+% here ends figure;
+pagefooter
+showpage
+%%Trailer
+%EOF
diff --git a/doc/qn_closed_multi_cs.fig b/doc/qn_closed_multi_cs.fig
new file mode 100644
index 0000000..14acd10
--- /dev/null
+++ b/doc/qn_closed_multi_cs.fig
@@ -0,0 +1,71 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6570 5580 180 180 6570 5580 6750 5580
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4770 5130 180 180 4770 5130 4950 5130
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6570 4680 180 180 6570 4680 6750 4680
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 6750 4590 7200 4590
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 6750 5490 7200 5490
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 5
+	0 0 1.00 60.00 120.00
+	 7200 5490 7200 4140 4230 4140 4230 5040 4590 5040
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 4950 5220 6120 5220
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 3
+	0 0 1.00 60.00 120.00
+	 6120 5220 6120 4770 6390 4770
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 3
+	0 0 1.00 60.00 120.00
+	 6120 5220 6120 5670 6390 5670
+2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 4950 5040 5850 5040
+2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 3
+	0 0 1.00 60.00 120.00
+	 5850 5040 5850 4590 6390 4590
+2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 3
+	0 0 1.00 60.00 120.00
+	 5850 5040 5850 5490 6390 5490
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 6750 4770 7380 4770
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 6750 5670 7380 5670
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 5
+	0 0 1.00 60.00 120.00
+	 7380 4770 7380 6120 4230 6120 4230 5220 4590 5220
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 3
+	0 0 1.00 60.00 120.00
+	 5310 5220 5310 4590 4230 4590
+2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 3
+	0 0 1.00 60.00 120.00
+	 5130 5040 5130 5670 4230 5670
+4 0 0 50 -1 0 10 0.0000 4 105 75 6525 5625 3\001
+4 0 0 50 -1 0 10 0.0000 4 105 75 4725 5175 1\001
+4 0 0 50 -1 0 10 0.0000 4 105 75 6525 4725 2\001
+4 0 0 50 -1 0 10 0.0000 4 105 120 6165 4950 .3\001
+4 0 0 50 -1 0 10 0.0000 4 105 120 5805 4545 .7\001
+4 0 0 50 -1 0 10 0.0000 4 105 120 5805 5670 .2\001
+4 0 0 50 -1 0 10 0.0000 4 105 120 6075 5850 .5\001
+4 0 0 50 -1 0 10 0.0000 4 105 120 5265 4545 .2\001
+4 0 0 50 -1 0 10 0.0000 4 105 120 5085 5850 .1\001
+4 0 0 50 -1 0 10 0.0000 4 105 450 4230 4095 Class 1\001
+4 0 0 50 -1 0 10 0.0000 4 105 450 4230 6300 Class 2\001
+4 0 0 50 -1 0 10 0.0000 4 105 315 4590 4905 CPU\001
+4 0 0 50 -1 0 10 0.0000 4 105 210 6480 5355 I/O\001
+4 0 0 50 -1 0 10 0.0000 4 105 210 6480 4455 I/O\001
+4 0 0 50 -1 0 10 0.0000 4 105 165 4680 5490 PS\001
+4 0 0 50 -1 0 10 0.0000 4 105 360 6390 4995 FCFS\001
+4 0 0 50 -1 0 10 0.0000 4 105 360 6390 5895 FCFS\001
diff --git a/doc/qn_closed_single.eps b/doc/qn_closed_single.eps
new file mode 100644
index 0000000..59b5525
--- /dev/null
+++ b/doc/qn_closed_single.eps
@@ -0,0 +1,216 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Title: qn_closed_single.fig
+%%Creator: fig2dev Version 3.2 Patchlevel 5d
+%%CreationDate: Mon Mar 10 11:38:03 2014
+%%BoundingBox: 0 0 172 133
+%Magnification: 1.0000
+%%EndComments
+%%BeginProlog
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+  bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+  4 -2 roll mul srgb} bind def
+ /DrawEllipse {
+	/endangle exch def
+	/startangle exch def
+	/yrad exch def
+	/xrad exch def
+	/y exch def
+	/x exch def
+	/savematrix mtrx currentmatrix def
+	x y tr xrad yrad sc 0 0 1 startangle endangle arc
+	closepath
+	savematrix setmatrix
+	} def
+
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+/pageheader {
+save
+newpath 0 133 moveto 0 0 lineto 172 0 lineto 172 133 lineto closepath clip newpath
+-299.7 380.8 translate
+1 -1 scale
+$F2psBegin
+10 setmiterlimit
+0 slj 0 slc
+ 0.06299 0.06299 sc
+} bind def
+/pagefooter {
+$F2psEnd
+restore
+} bind def
+%%EndProlog
+pageheader
+%
+% Fig objects follow
+%
+% 
+% here starts figure with depth 50
+% Ellipse
+7.500 slw
+n 6930 5580 270 270 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 6930 4500 270 270 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 5317 5043 270 270 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+0 slj
+0 slc
+gs  clippath
+6163 4530 m 6315 4530 l 6315 4470 l 6163 4470 l 6163 4470 l 6283 4500 l 6163 4530 l cp
+eoclip
+n 5580 5040 m 5850 5040 l 6030 4500 l
+ 6300 4500 l gs col0 s gr gr
+
+% arrowhead
+n 6163 4530 m 6283 4500 l 6163 4470 l  col0 s
+% Polyline
+gs  clippath
+6163 5610 m 6315 5610 l 6315 5550 l 6163 5550 l 6163 5550 l 6283 5580 l 6163 5610 l cp
+eoclip
+n 5850 5040 m 6030 5580 l
+ 6300 5580 l gs col0 s gr gr
+
+% arrowhead
+n 6163 5610 m 6283 5580 l 6163 5550 l  col0 s
+% Polyline
+gs  clippath
+4903 5070 m 5055 5070 l 5055 5010 l 4903 5010 l 4903 5010 l 5023 5040 l 4903 5070 l cp
+eoclip
+n 7470 5580 m 7470 3960 l 4770 3960 l 4770 5040 l
+ 5040 5040 l gs col0 s gr gr
+
+% arrowhead
+n 4903 5070 m 5023 5040 l 4903 5010 l  col0 s
+% Polyline
+gs  clippath
+7333 5610 m 7485 5610 l 7485 5550 l 7333 5550 l 7333 5550 l 7453 5580 l 7333 5610 l cp
+eoclip
+n 7200 5580 m
+ 7470 5580 l gs col0 s gr gr
+
+% arrowhead
+n 7333 5610 m 7453 5580 l 7333 5550 l  col0 s
+% Polyline
+n 6300 5310 m 6660 5310 l 6660 5850 l
+ 6300 5850 l gs col0 s gr 
+% Polyline
+n 6300 4230 m 6660 4230 l 6660 4770 l
+ 6300 4770 l gs col0 s gr 
+% Polyline
+gs  clippath
+7333 4530 m 7485 4530 l 7485 4470 l 7333 4470 l 7333 4470 l 7453 4500 l 7333 4530 l cp
+eoclip
+n 7200 4500 m
+ 7470 4500 l gs col0 s gr gr
+
+% arrowhead
+n 7333 4530 m 7453 4500 l 7333 4470 l  col0 s
+/Times-Roman ff 180.00 scf sf
+5895 5760 m
+gs 1 -1 sc (0.7) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5895 4455 m
+gs 1 -1 sc (0.3) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5175 5490 m
+gs 1 -1 sc (PS) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 4950 m
+gs 1 -1 sc (FCFS) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 6030 m
+gs 1 -1 sc (FCFS) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5130 4725 m
+gs 1 -1 sc (CPU) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 4185 m
+gs 1 -1 sc (Disk1) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 5265 m
+gs 1 -1 sc (Disk2) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5265 5085 m
+gs 1 -1 sc (1) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6885 4545 m
+gs 1 -1 sc (2) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6885 5625 m
+gs 1 -1 sc (3) col0 sh gr
+% here ends figure;
+pagefooter
+showpage
+%%Trailer
+%EOF
diff --git a/doc/qn_closed_single.fig b/doc/qn_closed_single.fig
new file mode 100644
index 0000000..1713e1e
--- /dev/null
+++ b/doc/qn_closed_single.fig
@@ -0,0 +1,42 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6930 5580 270 270 6930 5580 6930 5310
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6930 4500 270 270 6930 4500 6930 4230
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5317 5043 270 270 5317 5043 5317 4773
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	0 0 1.00 60.00 120.00
+	 5580 5040 5850 5040 6030 4500 6300 4500
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
+	0 0 1.00 60.00 120.00
+	 5850 5040 6030 5580 6300 5580
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 5
+	0 0 1.00 60.00 120.00
+	 7470 5580 7470 3960 4770 3960 4770 5040 5040 5040
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 7200 5580 7470 5580
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 6300 5310 6660 5310 6660 5850 6300 5850
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 6300 4230 6660 4230 6660 4770 6300 4770
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 7200 4500 7470 4500
+4 0 0 50 -1 0 12 0.0000 4 135 255 5895 5760 0.7\001
+4 0 0 50 -1 0 12 0.0000 4 135 255 5895 4455 0.3\001
+4 0 0 50 -1 0 12 0.0000 4 135 210 5175 5490 PS\001
+4 0 0 50 -1 0 12 0.0000 4 135 450 6705 4950 FCFS\001
+4 0 0 50 -1 0 12 0.0000 4 135 450 6705 6030 FCFS\001
+4 0 0 50 -1 0 12 0.0000 4 135 375 5130 4725 CPU\001
+4 0 0 50 -1 0 12 0.0000 4 135 480 6705 4185 Disk1\001
+4 0 0 50 -1 0 12 0.0000 4 135 480 6705 5265 Disk2\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 5265 5085 1\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 6885 4545 2\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 6885 5625 3\001
diff --git a/doc/qn_open_single.eps b/doc/qn_open_single.eps
new file mode 100644
index 0000000..7cb9e90
--- /dev/null
+++ b/doc/qn_open_single.eps
@@ -0,0 +1,240 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Title: qn_open_single.fig
+%%Creator: fig2dev Version 3.2 Patchlevel 5d
+%%CreationDate: Mon Mar 10 11:38:03 2014
+%%BoundingBox: 0 0 198 144
+%Magnification: 1.0000
+%%EndComments
+%%BeginProlog
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+  bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+  4 -2 roll mul srgb} bind def
+ /DrawEllipse {
+	/endangle exch def
+	/startangle exch def
+	/yrad exch def
+	/xrad exch def
+	/y exch def
+	/x exch def
+	/savematrix mtrx currentmatrix def
+	x y tr xrad yrad sc 0 0 1 startangle endangle arc
+	closepath
+	savematrix setmatrix
+	} def
+
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+/pageheader {
+save
+newpath 0 144 moveto 0 0 lineto 198 0 lineto 198 144 lineto closepath clip newpath
+-274.0 391.9 translate
+1 -1 scale
+$F2psBegin
+10 setmiterlimit
+0 slj 0 slc
+ 0.06299 0.06299 sc
+} bind def
+/pagefooter {
+$F2psEnd
+restore
+} bind def
+%%EndProlog
+pageheader
+%
+% Fig objects follow
+%
+% 
+% here starts figure with depth 50
+% Ellipse
+7.500 slw
+n 6930 5580 270 270 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 6930 4500 270 270 0 360 DrawEllipse gs col0 s gr
+
+% Ellipse
+n 5299 5029 270 270 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+0 slj
+0 slc
+gs  clippath
+6163 4530 m 6315 4530 l 6315 4470 l 6163 4470 l 6163 4470 l 6283 4500 l 6163 4530 l cp
+eoclip
+n 5580 5040 m 5850 5040 l 6030 4500 l
+ 6300 4500 l gs col0 s gr gr
+
+% arrowhead
+n 6163 4530 m 6283 4500 l 6163 4470 l  col0 s
+% Polyline
+gs  clippath
+6163 5610 m 6315 5610 l 6315 5550 l 6163 5550 l 6163 5550 l 6283 5580 l 6163 5610 l cp
+eoclip
+n 5850 5040 m 6030 5580 l
+ 6300 5580 l gs col0 s gr gr
+
+% arrowhead
+n 6163 5610 m 6283 5580 l 6163 5550 l  col0 s
+% Polyline
+gs  clippath
+7333 5610 m 7485 5610 l 7485 5550 l 7333 5550 l 7333 5550 l 7453 5580 l 7333 5610 l cp
+eoclip
+n 7200 5580 m
+ 7470 5580 l gs col0 s gr gr
+
+% arrowhead
+n 7333 5610 m 7453 5580 l 7333 5550 l  col0 s
+% Polyline
+n 6300 5310 m 6660 5310 l 6660 5850 l
+ 6300 5850 l gs col0 s gr 
+% Polyline
+n 6300 4230 m 6660 4230 l 6660 4770 l
+ 6300 4770 l gs col0 s gr 
+% Polyline
+gs  clippath
+7333 4530 m 7485 4530 l 7485 4470 l 7333 4470 l 7333 4470 l 7453 4500 l 7333 4530 l cp
+eoclip
+n 7200 4500 m
+ 7470 4500 l gs col0 s gr gr
+
+% arrowhead
+n 7333 4530 m 7453 4500 l 7333 4470 l  col0 s
+% Polyline
+gs  clippath
+4903 5070 m 5055 5070 l 5055 5010 l 4903 5010 l 4903 5010 l 5023 5040 l 4903 5070 l cp
+eoclip
+n 4500 5040 m
+ 5040 5040 l gs col0 s gr gr
+
+% arrowhead
+n 4903 5070 m 5023 5040 l 4903 5010 l  col0 s
+% Polyline
+gs  clippath
+4830 4903 m 4830 5055 l 4890 5055 l 4890 4903 l 4890 4903 l 4860 5023 l 4830 4903 l cp
+eoclip
+n 7470 5580 m 7470 3960 l 4860 3960 l
+ 4860 5040 l gs col0 s gr gr
+
+% arrowhead
+n 4830 4903 m 4860 5023 l 4890 4903 l  col0 s
+% Polyline
+gs  clippath
+5730 6073 m 5730 6225 l 5790 6225 l 5790 6073 l 5790 6073 l 5760 6193 l 5730 6073 l cp
+eoclip
+n 5760 5040 m
+ 5760 6210 l gs col0 s gr gr
+
+% arrowhead
+n 5730 6073 m 5760 6193 l 5790 6073 l  col0 s
+/Times-Roman ff 180.00 scf sf
+5445 5940 m
+gs 1 -1 sc (0.2) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5940 5760 m
+gs 1 -1 sc (0.5) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5940 4455 m
+gs 1 -1 sc (0.3) col0 sh gr
+/Symbol ff 180.00 scf sf
+4365 5130 m
+gs 1 -1 sc (l) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5130 4725 m
+gs 1 -1 sc (CPU) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 4185 m
+gs 1 -1 sc (Disk1) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 5265 m
+gs 1 -1 sc (Disk2) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5265 5085 m
+gs 1 -1 sc (1) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6885 4545 m
+gs 1 -1 sc (2) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6885 5625 m
+gs 1 -1 sc (3) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 6030 m
+gs 1 -1 sc (FCFS) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6705 4950 m
+gs 1 -1 sc (FCFS) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5220 5490 m
+gs 1 -1 sc (PS) col0 sh gr
+% here ends figure;
+pagefooter
+showpage
+%%Trailer
+%EOF
diff --git a/doc/qn_open_single.fig b/doc/qn_open_single.fig
new file mode 100644
index 0000000..6c4c35a
--- /dev/null
+++ b/doc/qn_open_single.fig
@@ -0,0 +1,50 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6930 5580 270 270 6930 5580 6930 5310
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6930 4500 270 270 6930 4500 6930 4230
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5299 5029 270 270 5299 5029 5299 4759
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	0 0 1.00 60.00 120.00
+	 5580 5040 5850 5040 6030 4500 6300 4500
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 3
+	0 0 1.00 60.00 120.00
+	 5850 5040 6030 5580 6300 5580
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 7200 5580 7470 5580
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 6300 5310 6660 5310 6660 5850 6300 5850
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 6300 4230 6660 4230 6660 4770 6300 4770
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 7200 4500 7470 4500
+2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 4500 5040 5040 5040
+2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 4
+	0 0 1.00 60.00 120.00
+	 7470 5580 7470 3960 4860 3960 4860 5040
+2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 5760 5040 5760 6210
+4 0 0 50 -1 0 12 0.0000 4 135 255 5445 5940 0.2\001
+4 0 0 50 -1 0 12 0.0000 4 135 255 5940 5760 0.5\001
+4 0 0 50 -1 0 12 0.0000 4 135 255 5940 4455 0.3\001
+4 0 0 50 -1 32 12 0.0000 4 150 105 4365 5130 l\001
+4 0 0 50 -1 0 12 0.0000 4 135 375 5130 4725 CPU\001
+4 0 0 50 -1 0 12 0.0000 4 135 480 6705 4185 Disk1\001
+4 0 0 50 -1 0 12 0.0000 4 135 480 6705 5265 Disk2\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 5265 5085 1\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 6885 4545 2\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 6885 5625 3\001
+4 0 0 50 -1 0 12 0.0000 4 135 450 6705 6030 FCFS\001
+4 0 0 50 -1 0 12 0.0000 4 135 450 6705 4950 FCFS\001
+4 0 0 50 -1 0 12 0.0000 4 135 210 5220 5490 PS\001
diff --git a/doc/qn_web_model.eps b/doc/qn_web_model.eps
new file mode 100644
index 0000000..a6bf7d3
--- /dev/null
+++ b/doc/qn_web_model.eps
@@ -0,0 +1,261 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Title: qn_web_model.fig
+%%Creator: fig2dev Version 3.2 Patchlevel 5d
+%%CreationDate: Mon Mar 10 11:38:03 2014
+%%BoundingBox: 0 0 285 146
+%Magnification: 1.0000
+%%EndComments
+%%BeginProlog
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+  bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+  4 -2 roll mul srgb} bind def
+ /DrawEllipse {
+	/endangle exch def
+	/startangle exch def
+	/yrad exch def
+	/xrad exch def
+	/y exch def
+	/x exch def
+	/savematrix mtrx currentmatrix def
+	x y tr xrad yrad sc 0 0 1 startangle endangle arc
+	closepath
+	savematrix setmatrix
+	} def
+
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+/pageheader {
+save
+newpath 0 146 moveto 0 0 lineto 285 0 lineto 285 146 lineto closepath clip newpath
+-294.0 386.5 translate
+1 -1 scale
+$F2psBegin
+10 setmiterlimit
+0 slj 0 slc
+ 0.06299 0.06299 sc
+} bind def
+/pagefooter {
+$F2psEnd
+restore
+} bind def
+%%EndProlog
+pageheader
+%
+% Fig objects follow
+%
+% 
+% here starts figure with depth 50
+% Ellipse
+7.500 slw
+n 5670 4860 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+0 slj
+0 slc
+n 5130 4680 m 5490 4680 l 5490 5040 l
+ 5130 5040 l gs col0 s gr 
+% Ellipse
+n 6930 5130 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+n 6390 4950 m 6750 4950 l 6750 5310 l
+ 6390 5310 l gs col0 s gr 
+% Ellipse
+n 6930 4590 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+n 6390 4410 m 6750 4410 l 6750 4770 l
+ 6390 4770 l gs col0 s gr 
+% Ellipse
+n 8280 4320 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+n 7740 4140 m 8100 4140 l 8100 4500 l
+ 7740 4500 l gs col0 s gr 
+% Ellipse
+n 8280 5400 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+n 7740 5220 m 8100 5220 l 8100 5580 l
+ 7740 5580 l gs col0 s gr 
+% Ellipse
+n 8280 4860 180 180 0 360 DrawEllipse gs col0 s gr
+
+% Polyline
+n 7740 4680 m 8100 4680 l 8100 5040 l
+ 7740 5040 l gs col0 s gr 
+% Polyline
+gs  clippath
+4993 4800 m 5145 4800 l 5145 4740 l 4993 4740 l 4993 4740 l 5113 4770 l 4993 4800 l cp
+eoclip
+n 8550 4770 m 9180 4770 l 9180 5940 l 4680 5940 l 4680 4770 l
+ 5130 4770 l gs col0 s gr gr
+
+% arrowhead
+n 4993 4800 m 5113 4770 l 4993 4740 l  col0 s
+% Polyline
+gs  clippath
+6163 4800 m 6315 4800 l 6315 4740 l 6163 4740 l 6163 4740 l 6283 4770 l 6163 4800 l cp
+eoclip
+n 5850 4770 m
+ 6300 4770 l gs col0 s gr gr
+
+% arrowhead
+n 6163 4800 m 6283 4770 l 6163 4740 l  col0 s
+% Polyline
+gs  clippath
+7513 4800 m 7665 4800 l 7665 4740 l 7513 4740 l 7513 4740 l 7633 4770 l 7513 4800 l cp
+eoclip
+n 7200 4770 m
+ 7650 4770 l gs col0 s gr gr
+
+% arrowhead
+n 7513 4800 m 7633 4770 l 7513 4740 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+4993 4980 m 5145 4980 l 5145 4920 l 4993 4920 l 4993 4920 l 5113 4950 l 4993 4980 l cp
+eoclip
+n 8550 4950 m 9000 4950 l 9000 5760 l 4860 5760 l 4860 4950 l
+ 5130 4950 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4993 4980 m 5113 4950 l 4993 4920 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+7513 4980 m 7665 4980 l 7665 4920 l 7513 4920 l 7513 4920 l 7633 4950 l 7513 4980 l cp
+eoclip
+n 7200 4950 m
+ 7650 4950 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 7513 4980 m 7633 4950 l 7513 4920 l  col0 s
+% Polyline
+ [60] 0 sd
+gs  clippath
+6163 4980 m 6315 4980 l 6315 4920 l 6163 4920 l 6163 4920 l 6283 4950 l 6163 4980 l cp
+eoclip
+n 5850 4950 m
+ 6300 4950 l gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 6163 4980 m 6283 4950 l 6163 4920 l  col0 s
+% Polyline
+n 6405 4050 m 6300 4050 6300 5565 105 arcto 4 {pop} repeat
+  6300 5670 7095 5670 105 arcto 4 {pop} repeat
+  7200 5670 7200 4155 105 arcto 4 {pop} repeat
+  7200 4050 6405 4050 105 arcto 4 {pop} repeat
+ cp gs col0 s gr 
+% Polyline
+n 7755 4050 m 7650 4050 7650 5565 105 arcto 4 {pop} repeat
+  7650 5670 8445 5670 105 arcto 4 {pop} repeat
+  8550 5670 8550 4155 105 arcto 4 {pop} repeat
+  8550 4050 7755 4050 105 arcto 4 {pop} repeat
+ cp gs col0 s gr 
+/Times-Roman ff 180.00 scf sf
+5625 4905 m
+gs 1 -1 sc (1) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6885 5175 m
+gs 1 -1 sc (3) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+6885 4635 m
+gs 1 -1 sc (2) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+6300 3960 m
+gs 1 -1 sc (App. Servers) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+8235 5445 m
+gs 1 -1 sc (6) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+8235 4905 m
+gs 1 -1 sc (5) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+8235 4365 m
+gs 1 -1 sc (4) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+7785 3960 m
+gs 1 -1 sc (DB Server) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5175 6120 m
+gs 1 -1 sc (Class 1) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+5175 5715 m
+gs 1 -1 sc (Class 2) col0 sh gr
+/Times-Roman ff 150.00 scf sf
+5040 4590 m
+gs 1 -1 sc (Web Server) col0 sh gr
+% here ends figure;
+pagefooter
+showpage
+%%Trailer
+%EOF
diff --git a/doc/qn_web_model.fig b/doc/qn_web_model.fig
new file mode 100644
index 0000000..2b72d9b
--- /dev/null
+++ b/doc/qn_web_model.fig
@@ -0,0 +1,72 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+6 5130 4680 5850 5040
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5670 4860 180 180 5670 4860 5850 4860
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 5130 4680 5490 4680 5490 5040 5130 5040
+-6
+6 6390 4950 7110 5310
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6930 5130 180 180 6930 5130 7110 5130
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 6390 4950 6750 4950 6750 5310 6390 5310
+-6
+6 6390 4410 7110 4770
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6930 4590 180 180 6930 4590 7110 4590
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 6390 4410 6750 4410 6750 4770 6390 4770
+-6
+6 7740 4140 8460 4500
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 8280 4320 180 180 8280 4320 8460 4320
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 7740 4140 8100 4140 8100 4500 7740 4500
+-6
+6 7740 5220 8460 5580
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 8280 5400 180 180 8280 5400 8460 5400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 7740 5220 8100 5220 8100 5580 7740 5580
+-6
+6 7740 4680 8460 5040
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 8280 4860 180 180 8280 4860 8460 4860
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+	 7740 4680 8100 4680 8100 5040 7740 5040
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 6
+	0 0 1.00 60.00 120.00
+	 8550 4770 9180 4770 9180 5940 4680 5940 4680 4770 5130 4770
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 5850 4770 6300 4770
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 7200 4770 7650 4770
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 6
+	0 0 1.00 60.00 120.00
+	 8550 4950 9000 4950 9000 5760 4860 5760 4860 4950 5130 4950
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 7200 4950 7650 4950
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2
+	0 0 1.00 60.00 120.00
+	 5850 4950 6300 4950
+2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5
+	 7200 5670 6300 5670 6300 4050 7200 4050 7200 5670
+2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5
+	 8550 5670 7650 5670 7650 4050 8550 4050 8550 5670
+4 0 0 50 -1 0 12 0.0000 4 135 105 5625 4905 1\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 6885 5175 3\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 6885 4635 2\001
+4 0 0 50 -1 0 10 0.0000 0 150 795 6300 3960 App. Servers\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 8235 5445 6\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 8235 4905 5\001
+4 0 0 50 -1 0 12 0.0000 4 135 105 8235 4365 4\001
+4 0 0 50 -1 0 10 0.0000 0 105 615 7785 3960 DB Server\001
+4 0 0 50 -1 0 12 0.0000 4 135 585 5175 6120 Class 1\001
+4 0 0 50 -1 0 12 0.0000 4 135 585 5175 5715 Class 2\001
+4 0 0 50 -1 0 10 0.0000 0 105 705 5040 4590 Web Server\001
diff --git a/doc/qu_closed_multi_apl.fig b/doc/qu_closed_multi_apl.fig
new file mode 100644
index 0000000..9c52a18
--- /dev/null
+++ b/doc/qu_closed_multi_apl.fig
@@ -0,0 +1,41 @@
+#FIG 3.2  Produced by xfig version 3.2.5b
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+6 5130 4770 5850 5670
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5490 4950 180 180 5490 4950 5670 4950
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5490 5490 180 180 5490 5490 5490 5310
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 5310 4950 5130 5220 5310 5490
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 5670 4950 5850 5220 5670 5490
+-6
+6 5130 3600 5850 4500
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5490 3780 180 180 5490 3780 5670 3780
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 5490 4320 180 180 5490 4320 5490 4140
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 5310 3780 5130 4050 5310 4320
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 5670 3780 5850 4050 5670 4320
+-6
+1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6570 4590 180 180 6570 4590 6750 4590
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 4
+	0 0 1.00 60.00 120.00
+	 5850 4050 6120 4050 6120 4500 6390 4500
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 4
+	0 0 1.00 60.00 120.00
+	 5850 5220 6120 5220 6120 4680 6390 4680
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 6
+	0 0 1.00 60.00 120.00
+	 6750 4680 7020 4680 7020 5940 4860 5940 4860 5220 5130 5220
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 6
+	0 0 1.00 60.00 120.00
+	 6750 4500 7020 4500 7020 3330 4860 3330 4860 4050 5130 4050
+4 0 0 50 -1 -1 10 0.0000 4 105 375 6345 4365 SYS 3\001
+4 0 0 50 -1 -1 10 0.0000 4 105 405 5265 3555 APL 1\001
+4 0 0 50 -1 -1 10 0.0000 4 105 390 5310 5805 IMS 2\001
diff --git a/doc/queueing.html b/doc/queueing.html
new file mode 100644
index 0000000..87ded96
--- /dev/null
+++ b/doc/queueing.html
@@ -0,0 +1,7116 @@
+<html lang="en">
+<head>
+<title>queueing</title>
+<meta http-equiv="Content-Type" content="text/html">
+<meta name="description" content="User manual for the queueing package, 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 package 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 © 2008, 2009, 2010, 2011, 2012, 2013, 2014 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>
+<ul>
+<li><a href="#About-the-Queueing-Package">1.1 About the Queueing Package</a>
+<li><a href="#Contributing-Guidelines">1.2 Contributing Guidelines</a>
+<li><a href="#Acknowledgements">1.3 Acknowledgements</a>
+</li></ul>
+<li><a name="toc_Installation-and-Getting-Started" href="#Installation-and-Getting-Started">2 Installation and Getting Started</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="#Development-sources">2.3 Development sources</a>
+<li><a href="#Naming-Conventions">2.4 Naming Conventions</a>
+<li><a href="#Quickstart-Guide">2.5 Quickstart Guide</a>
+</li></ul>
+<li><a name="toc_Markov-Chains" href="#Markov-Chains">3 Markov Chains</a>
+<ul>
+<li><a href="#Discrete_002dTime-Markov-Chains">3.1 Discrete-Time Markov Chains</a>
+<ul>
+<li><a href="#State-occupancy-probabilities-_0028DTMC_0029">3.1.1 State occupancy probabilities</a>
+<li><a href="#Birth_002ddeath-process-_0028DTMC_0029">3.1.2 Birth-death process</a>
+<li><a href="#Expected-number-of-visits-_0028DTMC_0029">3.1.3 Expected Number of Visits</a>
+<li><a href="#Time_002daveraged-expected-sojourn-times-_0028DTMC_0029">3.1.4 Time-averaged expected sojourn times</a>
+<li><a href="#Mean-time-to-absorption-_0028DTMC_0029">3.1.5 Mean Time to Absorption</a>
+<li><a href="#First-passage-times-_0028DTMC_0029">3.1.6 First Passage Times</a>
+</li></ul>
+<li><a href="#Continuous_002dTime-Markov-Chains">3.2 Continuous-Time Markov Chains</a>
+<ul>
+<li><a href="#State-occupancy-probabilities-_0028CTMC_0029">3.2.1 State occupancy probabilities</a>
+<li><a href="#Birth_002ddeath-process-_0028CTMC_0029">3.2.2 Birth-Death Process</a>
+<li><a href="#Expected-sojourn-times-_0028CTMC_0029">3.2.3 Expected Sojourn Times</a>
+<li><a href="#Time_002daveraged-expected-sojourn-times-_0028CTMC_0029">3.2.4 Time-Averaged Expected Sojourn Times</a>
+<li><a href="#Mean-time-to-absorption-_0028CTMC_0029">3.2.5 Mean Time to Absorption</a>
+<li><a href="#First-passage-times-_0028CTMC_0029">3.2.6 First Passage Times</a>
+</li></ul>
+</li></ul>
+<li><a name="toc_Single-Station-Queueing-Systems" href="#Single-Station-Queueing-Systems">4 Single Station Queueing Systems</a>
+<ul>
+<li><a href="#The-M_002fM_002f1-System">4.1 The M/M/1 System</a>
+<li><a href="#The-M_002fM_002fm-System">4.2 The M/M/m System</a>
+<li><a href="#The-Erlang_002dB-Formula">4.3 The Erlang-B Formula</a>
+<li><a href="#The-Erlang_002dC-Formula">4.4 The Erlang-C Formula</a>
+<li><a href="#The-Engset-Formula">4.5 The Engset Formula</a>
+<li><a href="#The-M_002fM_002finf-System">4.6 The M/M/inf System</a>
+<li><a href="#The-M_002fM_002f1_002fK-System">4.7 The M/M/1/K System</a>
+<li><a href="#The-M_002fM_002fm_002fK-System">4.8 The M/M/m/K System</a>
+<li><a href="#The-Asymmetric-M_002fM_002fm-System">4.9 The Asymmetric M/M/m System</a>
+<li><a href="#The-M_002fG_002f1-System">4.10 The M/G/1 System</a>
+<li><a href="#The-M_002fHm_002f1-System">4.11 The M/H_m/1 System</a>
+</li></ul>
+<li><a name="toc_Queueing-Networks" href="#Queueing-Networks">5 Queueing Networks</a>
+<ul>
+<li><a href="#Introduction-to-QNs">5.1 Introduction to QNs</a>
+<li><a href="#Single-Class-Models">5.2 Single Class Models</a>
+<ul>
+<li><a href="#Single-Class-Models">5.2.1 Open Networks</a>
+<li><a href="#Single-Class-Models">5.2.2 Closed Networks</a>
+<li><a href="#Single-Class-Models">5.2.3 Non Product-Form QNs</a>
+</li></ul>
+<li><a href="#Multiple-Class-Models">5.3 Multiple Class Models</a>
+<ul>
+<li><a href="#Multiple-Class-Models">5.3.1 Open Networks</a>
+<li><a href="#Multiple-Class-Models">5.3.2 Closed Networks</a>
+<li><a href="#Multiple-Class-Models">5.3.3 Mixed Networks</a>
+</li></ul>
+<li><a href="#Generic-Algorithms">5.4 Generic Algorithms</a>
+<li><a href="#Bounds-Analysis">5.5 Bounds Analysis</a>
+<li><a href="#QN-Analysis-Examples">5.6 QN Analysis Examples</a>
+<ul>
+<li><a href="#QN-Analysis-Examples">5.6.1 Closed, Single Class Network</a>
+<li><a href="#QN-Analysis-Examples">5.6.2 Open, Single Class Network</a>
+<li><a href="#QN-Analysis-Examples">5.6.3 Closed Multiclass Network/1</a>
+<li><a href="#QN-Analysis-Examples">5.6.4 Closed Multiclass Network/2</a>
+<li><a href="#QN-Analysis-Examples">5.6.5 Closed Multiclass Network/3</a>
+</li></ul>
+</li></ul>
+<li><a name="toc_References" href="#References">6 References</a>
+<li><a name="toc_Copying" href="#Copying">Appendix A 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: <a rel="next" accesskey="n" href="#Summary">Summary</a>,
+Up: <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 package. 
+It corresponds to version 1.2.3 of the package.
+
+<!--  -->
+<ul class="menu">
+<li><a accesskey="1" href="#Summary">Summary</a>
+<li><a accesskey="2" href="#Installation-and-Getting-Started">Installation and Getting Started</a>:  Installation of the queueing package. 
+<li><a accesskey="3" href="#Markov-Chains">Markov Chains</a>:                Functions for Markov Chains. 
+<li><a accesskey="4" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>:  Functions for single-station queueing systems. 
+<li><a accesskey="5" href="#Queueing-Networks">Queueing Networks</a>:            Functions for queueing networks. 
+<li><a accesskey="6" href="#References">References</a>:                   References
+<li><a accesskey="7" href="#Copying">Copying</a>:                      The GNU General Public License. 
+<li><a accesskey="8" href="#Concept-Index">Concept Index</a>:                An item for each concept. 
+<li><a accesskey="9" 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>
+
+<!--  -->
+<!-- This file has been automatically generated from summary.txi -->
+<!-- by proc.m. Do not edit this file, all changes will be lost -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Moreno Marzolla -->
+<!-- This file is part of the queueing package. -->
+<!-- The queueing package 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 package 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 package; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Summary"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Installation-and-Getting-Started">Installation and Getting Started</a>,
+Previous: <a rel="previous" accesskey="p" href="#Top">Top</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">1 Summary</h2>
+
+<ul class="menu">
+<li><a accesskey="1" href="#About-the-Queueing-Package">About the Queueing Package</a>:           What is the Queueing package
+<li><a accesskey="2" href="#Contributing-Guidelines">Contributing Guidelines</a>:              How to contribute
+<li><a accesskey="3" href="#Acknowledgements">Acknowledgements</a>
+</ul>
+
+<div class="node">
+<a name="About-the-Queueing-Package"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Contributing-Guidelines">Contributing Guidelines</a>,
+Up: <a rel="up" accesskey="u" href="#Summary">Summary</a>
+
+</div>
+
+<h3 class="section">1.1 About the Queueing Package</h3>
+
+<p>This document describes the <code>queueing</code> package for GNU Octave
+(<code>queueing</code> in short). The <code>queueing</code> package, 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>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:
+
+     <ul>
+<li>Birth-death process;
+<li>Transient and steady-state occupancy probabilities;
+<li>Mean times to absorption;
+<li>Expected sojourn times and time-averaged sojourn times;
+<li>Mean first passage times;
+
+   </ul>
+
+   <p>The <code>queueing</code> package 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> package 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–16, 2010, volume 6148
+of Lecture Notes in Computer Science, Springer, pp. 102–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.
+
+<div class="node">
+<a name="Contributing-Guidelines"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Acknowledgements">Acknowledgements</a>,
+Previous: <a rel="previous" accesskey="p" href="#About-the-Queueing-Package">About the Queueing Package</a>,
+Up: <a rel="up" accesskey="u" href="#Summary">Summary</a>
+
+</div>
+
+<h3 class="section">1.2 Contributing Guidelines</h3>
+
+<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 can be extracted and formatted into
+the printable manual. See the existing functions of the
+<code>queueing</code> package for the documentation style.
+
+     <li>Make sure that each new function
+properly checks the validity of its input parameters. For example,
+each function accepting vectors should check whether the dimensions
+match.
+
+     <li>Provide bibliographic references for each new algorithm you
+contribute. If your implementation differs in some way from the
+reference you give, please describe how and why your implementation
+differs. Add references to the <samp><span class="file">doc/references.txi</span></samp> file.
+
+     <li>Include test and demo blocks with your code. 
+Test blocks are particularly important, since most algorithms tend to
+be quite tricky to implement correctly. If appropriate, test blocks
+should also verify that the function fails on incorrect input
+parameters.
+
+   </ul>
+
+   <p>Send your contribution to Moreno Marzolla
+(<a href="mailto:moreno.marzolla at unibo.it">moreno.marzolla at unibo.it</a>). If you are just a user of this
+package and find it useful, let me know by dropping me a line. Thanks.
+
+<div class="node">
+<a name="Acknowledgements"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Contributing-Guidelines">Contributing Guidelines</a>,
+Up: <a rel="up" accesskey="u" href="#Summary">Summary</a>
+
+</div>
+
+<h3 class="section">1.3 Acknowledgements</h3>
+
+<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, Diego Didona,
+Yves Durand, Marco Guazzone, Michele Mazzucco, Dmitry Kolesnikov.
+
+<!-- This file has been automatically generated from installation.txi -->
+<!-- by proc.m. Do not edit this file, all changes will be lost -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014 Moreno Marzolla -->
+<!-- This file is part of the queueing package. -->
+<!-- The queueing package 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 package 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 package; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Installation-and-Getting-Started"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Markov-Chains">Markov Chains</a>,
+Previous: <a rel="previous" accesskey="p" href="#Summary">Summary</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">2 Installation and Getting Started</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="#Development-sources">Development sources</a>
+<li><a accesskey="4" href="#Naming-Conventions">Naming Conventions</a>
+<li><a accesskey="5" href="#Quickstart-Guide">Quickstart Guide</a>
+</ul>
+
+<div class="node">
+<a name="Installation-through-Octave-package-management-system"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Manual-installation">Manual installation</a>,
+Up: <a rel="up" accesskey="u" href="#Installation-and-Getting-Started">Installation and Getting Started</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.2.3 and can
+be downloaded from Octave-Forge
+
+   <p><a href="http://octave.sourceforge.net/queueing/">http://octave.sourceforge.net/queueing/</a>
+
+   <p>Additional information can be found at
+
+   <p><a href="http://www.moreno.marzolla.name/software/queueing/">http://www.moreno.marzolla.name/software/queueing/</a>
+
+   <p>To install <code>queueing</code>, follow these steps:
+
+     <ol type=1 start=1>
+
+     <li>If you have a recent version of GNU Octave and a network connection,
+you can install <code>queueing</code> directly from Octave command prompt
+using this command:
+
+     <pre class="example">          octave:1> <kbd>pkg install -forge queueing</kbd>
+</pre>
+     <p>The command above will automaticall download and install the latest
+version of the queueing package from Octave Forge, and install it on
+your machine.
+
+     <p>If you do not have root access, you can do a local install using:
+
+     <pre class="example">          octave:1> <kbd>pkg install -local -forge queueing</kbd>
+</pre>
+     <p>This will install <code>queueing</code> within your home directory, and the
+package will be available to your user only.
+
+     <li>Alternatively, you can first download <code>queueing</code> from
+Octave-Forge; then, to install the package in the system-wide
+location issue this command at the Octave prompt:
+
+     <pre class="example">          octave:1> <kbd>pkg install </kbd><em>queueing-1.2.3.tar.gz</em>
+</pre>
+     <p class="noindent">(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.
+
+     <p>If you do not have root access, you can do a local install using:
+
+     <pre class="example">          octave:1> <kbd>pkg install -local queueing-1.2.3.tar.gz</kbd>
+</pre>
+     <blockquote>
+<b>Note:</b> Octave version 3.2.3 as shipped with Ubuntu 10.04 LTS seems to ignore
+<samp><span class="option">-local</span></samp> and always tries to install the package on the system
+directory. 
+</blockquote>
+
+     <li>Verify that the package is indeed installed using the <kbd>pkg list</kbd>
+command at the Octave prompt; after succesfull installation you should
+see something like that:
+
+     <pre class="example">          octave:1><kbd>pkg list queueing</kbd>
+          Package Name  | Version | Installation directory
+          --------------+---------+-----------------------
+              queueing  |   1.2.3 | /home/moreno/octave/queueing-1.2.3
+</pre>
+     <li>Starting from version 1.1.1, <code>queueing</code> is no longer
+automatically loaded on Octave startup. To make the functions
+available for use, you need to issue the command
+
+     <pre class="example">          octave:1><kbd>pkg load queueing</kbd>
+</pre>
+     <p class="noindent">at the Octave prompt. To automatically load <code>queueing</code> each time
+Octave starts, you can add the command above to the startup script
+(usually, <samp><span class="file">~/.octaverc</span></samp> on Unix systems).
+
+     <li>To completely remove <code>queueing</code> from your system, use the
+<kbd>pkg uninstall</kbd> command:
+
+     <pre class="example">          octave:1> <kbd>pkg uninstall queueing</kbd>
+</pre>
+        </ol>
+
+<div class="node">
+<a name="Manual-installation"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Development-sources">Development sources</a>,
+Previous: <a rel="previous" accesskey="p" href="#Installation-through-Octave-package-management-system">Installation through Octave package management system</a>,
+Up: <a rel="up" accesskey="u" href="#Installation-and-Getting-Started">Installation and Getting Started</a>
+
+</div>
+
+<h3 class="section">2.2 Manual installation</h3>
+
+<p>If you want to manually install <code>queueing</code> in a custom location,
+you can download the tarball and unpack it somewhere:
+
+<pre class="example">     <kbd>tar xvfz queueing-1.2.3.tar.gz</kbd>
+     <kbd>cd queueing-1.2.3/queueing/</kbd>
+</pre>
+   <p>Copy all <code>.m</code> files from the <samp><span class="file">inst/</span></samp> directory to some
+target location. Then, 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><em>/usr/local/queueing</em>
+</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">/path/to/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.
+
+<!-- The following will not appear in the INSTALL text file -->
+<div class="node">
+<a name="Development-sources"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Naming-Conventions">Naming Conventions</a>,
+Previous: <a rel="previous" accesskey="p" href="#Manual-installation">Manual installation</a>,
+Up: <a rel="up" accesskey="u" href="#Installation-and-Getting-Started">Installation and Getting Started</a>
+
+</div>
+
+<h3 class="section">2.3 Development sources</h3>
+
+<p>The source code of the <code>queueing</code> package can be found in the
+Subversion repository at the URL:
+
+   <p><a href="http://octave.svn.sourceforge.net/viewvc/octave/trunk/octave-forge/main/queueing/">http://octave.svn.sourceforge.net/viewvc/octave/trunk/octave-forge/main/queueing/</a>
+
+   <p>The source distribution contains additional development files which
+are not present in the installation tarball. This section briefly
+describes the content of the source tree. This is only relevant for
+developers who want to modify the code or documentation; normal users
+of the <code>queueing</code> package don't need
+
+   <p>The source distribution contains the following directories:
+
+     <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 ‘<samp><span class="samp">qn</span></samp>’ prefix; the name of
+source files containing functions for Continuous-Time Markov Chains
+(CTMSs) start with the ‘<samp><span class="samp">ctmc</span></samp>’ prefix, and the names of files
+containing functions for Discrete-Time Markov Chains (DTMCs) start
+with the ‘<samp><span class="samp">dtmc</span></samp>’ 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">devel/</span></samp><dd>This directory contains function files which are either not working
+properly, or need additional testing before they are 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 ‘<samp><span class="samp">make</span></samp>’ (or ‘<samp><span class="samp">make all</span></samp>’) 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 ‘<samp><span class="samp">make check</span></samp>’ 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 ‘<samp><span class="samp">make clean</span></samp>’, ‘<samp><span class="samp">make distclean</span></samp>’ and ‘<samp><span class="samp">make dist</span></samp>’
+commands are used to clean up the source directory and prepare the
+distribution archive in compressed tar format.
+
+   </dl>
+
+<div class="node">
+<a name="Naming-Conventions"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Quickstart-Guide">Quickstart Guide</a>,
+Previous: <a rel="previous" accesskey="p" href="#Development-sources">Development sources</a>,
+Up: <a rel="up" accesskey="u" href="#Installation-and-Getting-Started">Installation and Getting Started</a>
+
+</div>
+
+<h3 class="section">2.4 Naming Conventions</h3>
+
+<p>Most of the functions in the <code>queueing</code> package obey a common
+naming convention. Function names are made of several parts; the first
+part is a prefix which indicates the class of problems the function
+addresses:
+
+     <dl>
+<dt><strong>ctmc-</strong><dd>Functions for continuous-time Markov chains
+
+     <br><dt><strong>dtmc-</strong><dd>Functions for discrete-time Markov chains
+
+     <br><dt><strong>qs-</strong><dd>Functions for analyzing queueing systems (individual service centers)
+
+     <br><dt><strong>qn-</strong><dd>Functions for analyzing queueing networks
+
+   </dl>
+
+   <p>Functions dealing with Markov chains start with either the <code>ctmc</code>
+or <code>dtmc</code> prefix; the prefix is optionally followed by an
+additional string which hints at what the function does:
+
+     <dl>
+<dt><strong>-bd</strong><dd>Birth-Death process
+
+     <br><dt><strong>-mtta</strong><dd>Mean Time to Absorption
+
+     <br><dt><strong>-fpt</strong><dd>First Passage Times
+
+     <br><dt><strong>-exps</strong><dd>Expected Sojourn Times
+
+     <br><dt><strong>-taexps</strong><dd>Time-Averaged Expected Sojourn Times
+
+   </dl>
+
+   <p>For example, function <code>ctmcbd</code> returns the infinitesimal
+generator matrix for a continuous birth-death process, while
+<code>dtmcbd</code> returns the transition probability matrix for a discrete
+birth-death process. Note that there exist functions <code>ctmc</code> and
+<code>dtmc</code> (without any suffix) that compute steady-state and
+transient state occupancy probabilities for CTMCs and DTMCs,
+respectively. See <a href="#Markov-Chains">Markov Chains</a>.
+
+   <p>Functions whose name starts with <code>qs-</code> deal with single station
+queueing systems. The suffix describes the type of system, e.g.,
+<code>qsmm1</code> for M/M/1, <code>qnmmm</code> for M/M/m and so
+on. See <a href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>.
+
+   <p>Finally, functions whose name starts with <code>qn-</code> deal with
+queueing networks. The character that follows indicates whether the
+function handles open (<code>'o'</code>) or closed (<code>'c'</code>) networks,
+and whether there is a single customer class (<code>'s'</code>) or multiple
+classes (<code>'m'</code>). The string <code>mix</code> indicates that the
+function supports mixed networks with both open and closed customer
+classes.
+
+     <dl>
+<dt><strong>-os-</strong><dd>Open, single-class network: open network with a single class of customers
+
+     <br><dt><strong>-om-</strong><dd>Open, multiclass network: open network with multiple job classes
+
+     <br><dt><strong>-cs-</strong><dd>Closed, single-class network
+
+     <br><dt><strong>-cm-</strong><dd>Closed, multiclass network
+
+     <br><dt><strong>-mix-</strong><dd>Mixed network with open and closed classes of customers
+
+   </dl>
+
+   <p>The last part of the function name indicates the algorithm implemented
+by the function. See <a href="#Queueing-Networks">Queueing Networks</a>.
+
+     <dl>
+<dt><strong>-aba</strong><dd>Asymptotic Bounds Analysis
+
+     <br><dt><strong>-bsb</strong><dd>Balanced System Bounds
+
+     <br><dt><strong>-gb</strong><dd>Geometric Bounds
+
+     <br><dt><strong>-pb</strong><dd>PB Bounds
+
+     <br><dt><strong>-cb</strong><dd>Composite Bounds (CB)
+
+     <br><dt><strong>-mva</strong><dd>Mean Value Analysis (MVA) algorithm
+
+     <br><dt><strong>-cmva</strong><dd>Conditional MVA
+
+     <br><dt><strong>-mvald</strong><dd>MVA with general load-dependent servers
+
+     <br><dt><strong>-mvaap</strong><dd>Approximate MVA
+
+     <br><dt><strong>-mvablo</strong><dd>MVABLO approximation for blocking queueing networks
+
+     <br><dt><strong>-conv</strong><dd>Convolution algorithm
+
+     <br><dt><strong>-convld</strong><dd>Convolution algorithm with general load-dependent servers
+
+   </dl>
+
+   <p><a name="index-deprecated-functions-1"></a>
+The current version (1.2.3) of the <code>queueing</code> package
+still supports the old function names (although they are no longer
+documented and will disappear in future releases). However, calling
+one of the deprecated functions results in a warning message being
+displayed; the message appears only one time per session:
+
+<pre class="example">     octave:1> <kbd>qnclosedab(10,[1 2 3])</kbd>
+         -| warning: qnclosedab is deprecated. Please use qncsaba instead
+         ⇒ ans =  0.16667
+</pre>
+   <p>Therefore, your legacy code should run unmodified with the current
+version of the <code>queueing</code> package. You can turn off all warning
+messages with the following command:
+
+<pre class="example">     octave:1> <kbd>warning ("off", "qn:deprecated-function");</kbd>
+</pre>
+   <p>However, it is recommended to update to the new API and not ignore the
+warnings above. To help you catch usages of deprecated functions, even
+with applications which produce a lot of console output, you can
+transform warnings into errors so that your application will stop
+immediately:
+
+<pre class="example">     octave:1> <kbd>warning ("error", "qn:deprecated-function");</kbd>
+</pre>
+   <div class="node">
+<a name="Quickstart-Guide"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Naming-Conventions">Naming Conventions</a>,
+Up: <a rel="up" accesskey="u" href="#Installation-and-Getting-Started">Installation and Getting Started</a>
+
+</div>
+
+<h3 class="section">2.5 Quickstart Guide</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. Extensive
+documentation is provided for each function, and can be displayed with
+the <samp><span class="command">help</span></samp> command. For example:
+
+<pre class="example">     octave:2> <kbd>help qncsmvablo</kbd>
+</pre>
+   <p class="noindent">prints the documentation for the <samp><span class="command">qncsmvablo</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>Many functions have demo blocks containing code snippets which
+illustrate how that function can be used. For example, to execute the
+demos for the <samp><span class="command">qnclosed</span></samp> function, use the <samp><span class="command">demo</span></samp>
+command as follows:
+
+<pre class="example">     octave:4> <kbd>demo qnclosed</kbd>
+</pre>
+   <p>Here we illustrate some basic usage of the <code>queueing</code> package by
+considering a few examples.
+
+<p class="noindent"><strong>Example 1</strong>
+Compute the stationary state occupancy probabilities of a continuous-time
+Markov chain with infinitesimal generator matrix
+
+<pre class="example">         / -0.8   0.6   0.2 \
+     Q = |  0.3  -0.7   0.4 |
+         \  0.2   0.2  -0.4 /
+</pre>
+   <pre class="example">     Q = [ -0.8  0.6  0.2; \
+            0.3 -0.7  0.4; \
+            0.2  0.2 -0.4 ];
+     q = ctmc(Q)
+         ⇒ q = 0.23256   0.32558   0.44186
+</pre>
+   <p class="noindent"><strong>Example 2</strong>
+Compute the transient state occupancy probability after n=3
+transitions of a three state discrete time birth-death process, with
+birth probabilities \lambda_01 = 0.3 and \lambda_12 =
+0.5 and death probabilities \mu_10 = 0.5 and \mu_21
+= 0.7, assuming that the system is initially in state zero (i.e., the
+initial state occupancy probabilities are (1, 0, 0)).
+
+<pre class="example">     n = 3;
+     p0 = [1 0 0];
+     P = dtmcbd( [0.3 0.5], [0.5 0.7] );
+     p = dtmc(P,n,p0)
+         ⇒ p = 0.55300   0.29700   0.15000
+</pre>
+   <p class="noindent"><strong>Example 3</strong>
+Compute server utilization, response time, mean number of requests and
+throughput of a closed queueing network with N=4 requests and
+three M/M/1–FCFS queues with mean service times \bf S
+= (1.0, 0.8, 1.4) and average number of visits \bf V = (1.0,
+0.8, 0.8)
+
+<pre class="example">     S = [1.0 0.8 1.4];
+     V = [1.0 0.8 0.8];
+     N = 4;
+     [U R Q X] = qncsmva(N, S, V)
+         ⇒
+          U = 0.70064   0.44841   0.78471
+          R = 2.1030    1.2642    3.2433
+          Q = 1.47346   0.70862   1.81792
+          X = 0.70064   0.56051   0.56051
+</pre>
+   <p class="noindent"><strong>Example 4</strong>
+Compute server utilization, response time, mean number of requests and
+throughput of an open queueing network with three M/M/1–FCFS
+queues with mean service times \bf S = (1.0, 0.8, 1.4) and
+average number of visits \bf V = (1.0, 0.8, 0.8). The overall
+arrival rate is \lambda = 0.8 req/s
+
+<pre class="example">     S = [1.0 0.8 1.4];
+     V = [1.0 0.8 0.8];
+     lambda = 0.8;
+     [U R Q X] = qnos(lambda, S, V)
+         ⇒
+          U = 0.80000   0.51200   0.89600
+          R = 5.0000    1.6393   13.4615
+          Q = 4.0000    1.0492    8.6154
+          X = 0.80000   0.64000   0.64000
+</pre>
+   <!-- This file has been automatically generated from markovchains.txi -->
+<!-- by proc.m. Do not edit this file, all changes will be lost -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014 Moreno Marzolla -->
+<!-- This file is part of the queueing package. -->
+<!-- The queueing package 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 package 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 package; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Markov-Chains"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>,
+Previous: <a rel="previous" accesskey="p" href="#Installation-and-Getting-Started">Installation and Getting Started</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">3 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: <a rel="next" accesskey="n" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>,
+Up: <a rel="up" accesskey="u" href="#Markov-Chains">Markov Chains</a>
+
+</div>
+
+<h3 class="section">3.1 Discrete-Time Markov Chains</h3>
+
+<p>Let X_0, X_1, <small class="dots">...</small>, X_n, <small class="dots">...</small>  be a sequence of random
+variables defined over a discete state space 1, 2,
+<small class="dots">...</small>. The sequence X_0, X_1, <small class="dots">...</small>, X_n, <small class="dots">...</small>  is a
+<em>stochastic process</em> with discrete time 0, 1, 2,
+<small class="dots">...</small>. A <em>Markov chain</em> is a stochastic process {X_n,
+n=0, 1, 2, <small class="dots">...</small>} which satisfies the following Markov property:
+
+   <p>P(X_n+1 = x_n+1 | X_n = x_n, X_n-1 = x_n-1, ..., X_0 = x_0) = P(X_n+1 = x_n+1 | X_n = x_n)
+
+<p class="noindent">which basically means that the probability that the system is in
+a particular state at time n+1 only depends on the state the
+system was at time n.
+
+   <p>The evolution of a Markov chain with finite state space {1, 2,
+<small class="dots">...</small>, N} can be fully described by a stochastic matrix \bf
+P(n) = [ P_i,j(n) ] such that P_i, j(n) = P( X_n+1 = j\
+|\ X_n = i ).  If the Markov chain is homogeneous (that is, the
+transition probability matrix \bf P(n) is time-independent),
+we can write \bf P = [P_i, j], where P_i, j = P(
+X_n+1 = j\ |\ X_n = i ) for all n=0, 1, <small class="dots">...</small>.
+
+   <p>The transition probability matrix \bf P must satisfy the
+following two properties: (1) P_i, j ≥ 0 for all
+i, j, and (2) \sum_j=1^N P_i,j = 1 for all i
+
+   <p><a name="doc_002ddtmcchkP"></a>
+
+<div class="defun">
+— Function File: [<var>r</var> <var>err</var>] = <b>dtmcchkP</b> (<var>P</var>)<var><a name="index-dtmcchkP-2"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-discrete-time-3"></a><a name="index-DTMC-4"></a><a name="index-discrete-time-Markov-chain-5"></a>
+Check whether <var>P</var> is a valid transition probability matrix.
+
+        <p>If <var>P</var> is valid, <var>r</var> is the size (number of rows or columns)
+of <var>P</var>. If <var>P</var> is not a transition probability matrix,
+<var>r</var> is set to zero, and <var>err</var> to an appropriate error string.
+
+        </blockquote></div>
+
+<ul class="menu">
+<li><a accesskey="1" href="#State-occupancy-probabilities-_0028DTMC_0029">State occupancy probabilities (DTMC)</a>
+<li><a accesskey="2" href="#Birth_002ddeath-process-_0028DTMC_0029">Birth-death process (DTMC)</a>
+<li><a accesskey="3" href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a>
+<li><a accesskey="4" href="#Time_002daveraged-expected-sojourn-times-_0028DTMC_0029">Time-averaged expected sojourn times (DTMC)</a>
+<li><a accesskey="5" href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a>
+<li><a accesskey="6" href="#First-passage-times-_0028DTMC_0029">First passage times (DTMC)</a>
+</ul>
+
+<div class="node">
+<a name="State-occupancy-probabilities-(DTMC)"></a>
+<a name="State-occupancy-probabilities-_0028DTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Birth_002ddeath-process-_0028DTMC_0029">Birth-death process (DTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.1.1 State occupancy probabilities</h4>
+
+<p>Given a discrete-time Markov chain with state spate {1, 2,
+<small class="dots">...</small>, N}, we denote with \bf \pi(n) = \left(\pi_1(n),
+\pi_2(n), <small class="dots">...</small>, \pi_N(n) \right) the <em>state occupancy
+probability vector</em> at step n, n = 0, 1, <small class="dots">...</small>. 
+\pi_i(n) denotes the probability that the system is in state
+i after n transitions.
+
+   <p>Given the transition probability matrix \bf P and the initial
+state occupancy probability vector \bf \pi(0) =
+\left(\pi_1(0), \pi_2(0), <small class="dots">...</small>, \pi_N(0)\right), \bf
+\pi(n) can be computed as:
+
+<pre class="example">     \pi(n) = \pi(0) P^n
+</pre>
+   <p>Under certain conditions, there exists a <em>stationary state
+occupancy probability</em> \bf \pi = \lim_n \rightarrow +\infty
+\bf \pi(n), which is independent from \bf \pi(0). The
+stationary vector \bf \pi is the solution of the following
+linear system:
+
+<pre class="example">     /
+     | \pi P   = \pi
+     | \pi 1^T = 1
+     \
+</pre>
+   <p class="noindent">where \bf 1 is the row vector of ones, and ( \cdot )^T
+the transpose operator.
+
+   <p><a name="doc_002ddtmc"></a>
+
+<div class="defun">
+— Function File: <var>p</var> = <b>dtmc</b> (<var>P</var>)<var><a name="index-dtmc-6"></a></var><br>
+— Function File: <var>p</var> = <b>dtmc</b> (<var>P, n, p0</var>)<var><a name="index-dtmc-7"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-discrete-time-8"></a><a name="index-discrete-time-Markov-chain-9"></a><a name="index-DTMC-10"></a><a name="index-Markov-chain_002c-stationary-probabilities-11"></a><a name="index-Markov-chain_002c-transient-probabilities-12"></a>
+Compute stationary or transient state occupancy probabilities for a discrete-time Markov chain.
+
+        <p>With a single argument, compute the stationary state occupancy
+probability vector <var>p</var><code>(1), ..., </code><var>p</var><code>(N)</code> for a
+discrete-time Markov chain with state space {1, 2, <small class="dots">...</small>,
+N} and with N \times N transition probability matrix
+<var>P</var>. With three arguments, compute the transient state occupancy
+vector <var>p</var><code>(1), ..., </code><var>p</var><code>(N)</code> that the system is in
+state i after <var>n</var> steps, given initial occupancy
+probabilities <var>p0</var>(1), <small class="dots">...</small>, <var>p0</var>(N).
+
+        <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>Number of transitions after which compute the state occupancy probabilities
+(n=0, 1, <small class="dots">...</small>)
+
+          <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 called with a single argument, <var>p</var><code>(i)</code>
+is the steady-state probability that the system is in state i. 
+If this function is called with three arguments, <var>p</var><code>(i)</code>
+is the probability that the system is in state i
+after <var>n</var> transitions, given the initial probabilities
+<var>p0</var><code>(i)</code> that the initial state is i.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> ctmc.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+   <p>This example is from <a href="#GrSn97">GrSn97</a>. Let us consider a maze with nine
+rooms, as shown in the following figure
+
+<pre class="example">     +-----+-----+-----+
+     |     |     |     |
+     |  1     2     3  |
+     |     |     |     |
+     +-   -+-   -+-   -+
+     |     |     |     |
+     |  4     5     6  |
+     |     |     |     |
+     +-   -+-   -+-   -+
+     |     |     |     |
+     |  7     8     9  |
+     |     |     |     |
+     +-----+-----+-----+
+</pre>
+   <p>A mouse is placed in one of the rooms and can wander around. At each
+step, the mouse moves from the current room to a neighboring one with
+equal probability: if it is in room 1, it can move to room 2 and 4
+with probability 1/2, respectively. If the mouse is in room 8, it can
+move to either 7, 5 or 9 with probability 1/3.
+
+   <p>The transition probability \bf P from room i to room
+j is the following:
+
+<pre class="example">             / 0     1/2   0     1/2   0     0     0     0     0   \
+             | 1/3   0     1/3   0     1/3   0     0     0     0   |
+             | 0     1/2   0     0     0     1/2   0     0     0   |
+             | 1/3   0     0     0     1/3   0     1/3   0     0   |
+         P = | 0     1/4   0     1/4   0     1/4   0     1/4   0   |
+             | 0     0     1/3   0     1/3   0     0     0     1/3 |
+             | 0     0     0     1/2   0     0     0     1/2   0   |
+             | 0     0     0     0     1/3   0     1/3   0     1/3 |
+             \ 0     0     0     0     0     1/2   0     1/2   0   /
+</pre>
+   <p>The stationary state occupancy probability vector can be computed
+using the following code:
+
+<pre class="example"><pre class="verbatim">      P = zeros(9,9);
+      P(1,[2 4]    ) = 1/2;
+      P(2,[1 5 3]  ) = 1/3;
+      P(3,[2 6]    ) = 1/2;
+      P(4,[1 5 7]  ) = 1/3;
+      P(5,[2 4 6 8]) = 1/4;
+      P(6,[3 5 9]  ) = 1/3;
+      P(7,[4 8]    ) = 1/2;
+      P(8,[7 5 9]  ) = 1/3;
+      P(9,[6 8]    ) = 1/2;
+      p = dtmc(P);
+      disp(p)
+</pre>
+         ⇒ 0.083333   0.125000   0.083333   0.125000
+            0.166667   0.125000   0.083333   0.125000
+            0.083333
+</pre>
+   <div class="node">
+<a name="Birth-death-process-(DTMC)"></a>
+<a name="Birth_002ddeath-process-_0028DTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a>,
+Previous: <a rel="previous" accesskey="p" href="#State-occupancy-probabilities-_0028DTMC_0029">State occupancy probabilities (DTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.1.2 Birth-death process</h4>
+
+<p><a name="doc_002ddtmcbd"></a>
+
+<div class="defun">
+— Function File: <var>P</var> = <b>dtmcbd</b> (<var>b, d</var>)<var><a name="index-dtmcbd-13"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-discrete-time-14"></a><a name="index-DTMC-15"></a><a name="index-discrete-time-Markov-chain-16"></a><a name="index-birth_002ddeath-process_002c-DTMC-17"></a>
+Returns the transition probability matrix P for a discrete
+birth-death process over state space 1, 2, <small class="dots">...</small>, N. 
+<var>b</var><code>(i)</code> is the transition probability from state
+i to i+1, and <var>d</var><code>(i)</code> is the transition
+probability from state i+1 to state i, i=1, 2,
+<small class="dots">...</small>, N-1.
+
+        <p>Matrix \bf P is therefore defined as:
+
+     <pre class="example">          /                                                             \
+          | 1-b(1)     b(1)                                             |
+          |  d(1)  (1-d(1)-b(2))     b(2)                               |
+          |            d(2)      (1-d(2)-b(3))     b(3)                 |
+          |                                                             |
+          |                 ...           ...          ...              |
+          |                                                             |
+          |                         d(N-2)   (1-d(N-2)-b(N-1))  b(N-1)  |
+          |                                        d(N-1)      1-d(N-1) |
+          \                                                             /
+</pre>
+        <p class="noindent">where \lambda_i and \mu_i are the birth and
+death probabilities, respectively.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> ctmcbd.
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Expected-number-of-visits-(DTMC)"></a>
+<a name="Expected-number-of-visits-_0028DTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Time_002daveraged-expected-sojourn-times-_0028DTMC_0029">Time-averaged expected sojourn times (DTMC)</a>,
+Previous: <a rel="previous" accesskey="p" href="#Birth_002ddeath-process-_0028DTMC_0029">Birth-death process (DTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.1.3 Expected Number of Visits</h4>
+
+<p>Given a N state discrete-time Markov chain with transition
+matrix \bf P and an integer n ≥ 0, we let
+L_i(n) be the the expected number of visits to state i
+during the first n transitions. The vector \bf L(n) =
+( L_1(n), L_2(n), <small class="dots">...</small>, L_N(n) ) is defined as
+
+<pre class="example">              n            n
+             ___          ___
+            \            \           i
+     L(n) =  >   pi(i) =  >   pi(0) P
+            /___         /___
+             i=0          i=0
+</pre>
+   <p class="noindent">where \bf \pi(i) = \bf \pi(0)\bf P^i is the state
+occupancy probability after i transitions.
+
+   <p>If \bf P is absorbing, i.e., the stochastic process eventually
+reaches a state with no outgoing transitions with probability 1, then
+we can compute the expected number of visits until absorption
+\bf L. To do so, we first rearrange the states to rewrite
+matrix \bf P as:
+
+<pre class="example">         / Q | R \
+     P = |---+---|
+         \ 0 | I /
+</pre>
+   <p class="noindent">where the first t states are transient
+and the last r states are absorbing (t+r = N). The
+matrix \bf N = (\bf I - \bf Q)^-1 is called the
+<em>fundamental matrix</em>; N_i,j is the expected number of
+times that the process is in the j-th transient state if it
+started in the i-th transient state. If we reshape \bf N
+to the size of \bf P (filling missing entries with zeros), we
+have that, for absorbing chains \bf L = \bf \pi(0)\bf N.
+
+   <p><a name="doc_002ddtmcexps"></a>
+
+<div class="defun">
+— Function File: <var>L</var> = <b>dtmcexps</b> (<var>P, n, p0</var>)<var><a name="index-dtmcexps-18"></a></var><br>
+— Function File: <var>L</var> = <b>dtmcexps</b> (<var>P, p0</var>)<var><a name="index-dtmcexps-19"></a></var><br>
+<blockquote>
+        <p><a name="index-expected-sojourn-times_002c-DTMC-20"></a><a name="index-DTMC-21"></a><a name="index-discrete-time-Markov-chain-22"></a><a name="index-Markov-chain_002c-discrete-time-23"></a>
+Compute the expected number of visits to each state during the first
+<var>n</var> transitions, or until abrosption.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd>N \times N transition probability matrix.
+
+          <br><dt><var>n</var><dd>Number of steps during which the expected number of visits are
+computed (<var>n</var> ≥ 0). If <var>n</var><code>=0</code>, returns
+<var>p0</var>. If <var>n</var><code> > 0</code>, returns the expected number of
+visits after exactly <var>n</var> transitions.
+
+          <br><dt><var>p0</var><dd>Initial state occupancy probability.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>L</var><dd>When called with two arguments, <var>L</var><code>(i)</code> is the expected
+number of visits to transient state i before absorption. When
+called with three arguments, <var>L</var><code>(i)</code> is the expected number
+of visits to state i during the first <var>n</var> transitions,
+given initial occupancy probability <var>p0</var>.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> ctmcexps.
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Time-averaged-expected-sojourn-times-(DTMC)"></a>
+<a name="Time_002daveraged-expected-sojourn-times-_0028DTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a>,
+Previous: <a rel="previous" accesskey="p" href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.1.4 Time-averaged expected sojourn times</h4>
+
+<p><a name="doc_002ddtmctaexps"></a>
+
+<div class="defun">
+— Function File: <var>L</var> = <b>dtmctaexps</b> (<var>P, n, p0</var>)<var><a name="index-dtmctaexps-24"></a></var><br>
+— Function File: <var>L</var> = <b>dtmctaexps</b> (<var>P, p0</var>)<var><a name="index-dtmctaexps-25"></a></var><br>
+<blockquote>
+        <p><a name="index-time_002dalveraged-sojourn-time_002c-DTMC-26"></a><a name="index-discrete-time-Markov-chain-27"></a><a name="index-Markov-chain_002c-discrete-time-28"></a><a name="index-DTMC-29"></a>
+Compute the <em>time-averaged sojourn time</em> <var>M</var><code>(i)</code>,
+defined as the fraction of time steps {0, 1, <small class="dots">...</small>, n} (or
+until absorption) spent in state i, assuming that the state
+occupancy probabilities at time 0 are <var>p0</var>.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd>N \times N transition probability matrix.
+
+          <br><dt><var>n</var><dd>Number of transitions during which the time-averaged expected sojourn times
+are computed (<var>n</var> ≥ 0). if <var>n</var> = 0,
+returns <var>p0</var>.
+
+          <br><dt><var>p0</var><dd>Initial state occupancy probabilities.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>M</var><dd>If this function is called with three arguments, <var>M</var><code>(i)</code> is
+the expected fraction of steps {0, 1, <small class="dots">...</small>, n} spent in
+state i, assuming that the state occupancy probabilities at
+time zero are <var>p0</var>. If this function is called with two
+arguments, <var>M</var><code>(i)</code> is the expected fraction of steps spent
+in state i until absorption.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> ctmctaexps.
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Mean-time-to-absorption-(DTMC)"></a>
+<a name="Mean-time-to-absorption-_0028DTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#First-passage-times-_0028DTMC_0029">First passage times (DTMC)</a>,
+Previous: <a rel="previous" accesskey="p" href="#Time_002daveraged-expected-sojourn-times-_0028DTMC_0029">Time-averaged expected sojourn times (DTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.1.5 Mean Time to Absorption</h4>
+
+<p>The <em>mean time to absorption</em> is defined as the average number of
+transitions which are required to reach an absorbing state, starting
+from a transient state (or given an initial state occupancy
+probability vector \bf \pi(0)).
+
+   <p>Let \bf t_i be the expected number of transitions before
+being absorbed in any absorbing state, starting from state i. 
+Vector \bf t can be computed from the fundamental matrix
+\bf N (see <a href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a>) as
+
+<pre class="example">     t = 1 N
+</pre>
+   <p>Let \bf B = [ B_i, j ] be a matrix where B_i, j is
+the probability of being absorbed in state j, starting from
+transient state i. Again, using matrices \bf N and
+\bf R (see <a href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a>) we can write
+
+<pre class="example">     B = N R
+</pre>
+   <p><a name="doc_002ddtmcmtta"></a>
+
+<div class="defun">
+— Function File: [<var>t</var> <var>N</var> <var>B</var>] = <b>dtmcmtta</b> (<var>P</var>)<var><a name="index-dtmcmtta-30"></a></var><br>
+— Function File: [<var>t</var> <var>N</var> <var>B</var>] = <b>dtmcmtta</b> (<var>P, p0</var>)<var><a name="index-dtmcmtta-31"></a></var><br>
+<blockquote>
+        <p><a name="index-mean-time-to-absorption_002c-DTMC-32"></a><a name="index-absorption-probabilities_002c-DTMC-33"></a><a name="index-fundamental-matrix-34"></a><a name="index-DTMC-35"></a><a name="index-discrete-time-Markov-chain-36"></a><a name="index-Markov-chain_002c-discrete-time-37"></a>
+Compute the expected number of steps before absorption for a
+DTMC with N \times N transition probability matrix <var>P</var>;
+compute also the fundamental matrix <var>N</var> for <var>P</var>.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd>N \times N transition probability matrix.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>t</var><dd>When called with a single argument, <var>t</var> is a vector of size
+N such that <var>t</var><code>(i)</code> is the expected number of steps
+before being absorbed in any absorbing state, starting from state
+i; if i is absorbing, <var>t</var><code>(i) = 0</code>. When
+called with two arguments, <var>t</var> is a scalar, and represents the
+expected number of steps before absorption, starting from the initial
+state occupancy probability <var>p0</var>.
+
+          <br><dt><var>N</var><dd>When called with a single argument, <var>N</var> is the N \times N
+fundamental matrix for <var>P</var>. <var>N</var><code>(i,j)</code> is the expected
+number of visits to transient state <var>j</var> before absorption, if it
+is started in transient state <var>i</var>. The initial state is counted
+if i = j. When called with two arguments, <var>N</var> is a vector
+of size N such that <var>N</var><code>(j)</code> is the expected number
+of visits to transient state <var>j</var> before absorption, given initial
+state occupancy probability <var>P0</var>.
+
+          <br><dt><var>B</var><dd>When called with a single argument, <var>B</var> is a N \times N
+matrix where <var>B</var><code>(i,j)</code> is the probability of being absorbed
+in state j, starting from transient state i; if
+j is not absorbing, <var>B</var><code>(i,j) = 0</code>; if i
+is absorbing, <var>B</var><code>(i,i) = 1</code> and
+<var>B</var><code>(i,j) = 0</code> for all j \neq j. When called with
+two arguments, <var>B</var> is a vector of size N where
+<var>B</var><code>(j)</code> is the probability of being absorbed in state
+<var>j</var>, given initial state occupancy probabilities <var>p0</var>.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> ctmcmtta.
+
+        </blockquote></div>
+
+<div class="node">
+<a name="First-passage-times-(DTMC)"></a>
+<a name="First-passage-times-_0028DTMC_0029"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.1.6 First Passage Times</h4>
+
+<p>The First Passage Time M_i, j is 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 + >   P_ij * M_kj
+               /___
+               k!=j
+</pre>
+   <p>To compute \bf M = [ M_i, j] a different formulation is
+used.  Let \bf W be the N \times N matrix having each
+row equal to the stationary state occupancy probability vector
+\bf \pi for \bf P; let \bf I be the N
+\times N identity matrix. Define \bf Z as follows:
+
+<pre class="example">                    -1
+     Z = (I - P + W)
+</pre>
+   <p class="noindent">Then, we have that
+
+<pre class="example">            Z_jj - Z_ij
+     M_ij = -----------
+               \pi_j
+</pre>
+   <p>According to the definition above, M_i,i = 0. We arbitrarily
+let M_i,i to be the <em>mean recurrence time</em> r_i
+for state i, that is the average number of transitions needed
+to return to state i starting from it. r_i is:
+
+<pre class="example">             1
+     r_i = -----
+           \pi_i
+</pre>
+   <p><a name="doc_002ddtmcfpt"></a>
+
+<div class="defun">
+— Function File: <var>M</var> = <b>dtmcfpt</b> (<var>P</var>)<var><a name="index-dtmcfpt-38"></a></var><br>
+<blockquote>
+        <p><a name="index-first-passage-times-39"></a><a name="index-mean-recurrence-times-40"></a><a name="index-discrete-time-Markov-chain-41"></a><a name="index-Markov-chain_002c-discrete-time-42"></a><a name="index-DTMC-43"></a>
+Compute mean first passage times and mean recurrence times
+for an irreducible discrete-time Markov chain.
+
+        <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.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>M</var><dd>For all i \neq j, <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>. <var>M</var><code>(i,i)</code> is the <em>mean
+recurrence time</em> of state i, and represents the average time
+needed to return to state <var>i</var>.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> ctmcfpt.
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Continuous-Time-Markov-Chains"></a>
+<a name="Continuous_002dTime-Markov-Chains"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a>,
+Up: <a rel="up" accesskey="u" href="#Markov-Chains">Markov Chains</a>
+
+</div>
+
+<h3 class="section">3.2 Continuous-Time Markov Chains</h3>
+
+<p>A stochastic process {X(t), t ≥ 0} is a continuous-time
+Markov chain if, for all integers n, and for any sequence
+t_0, t_1 , \ldots, t_n, t_n+1 such that t_0 < t_1 <
+\ldots < t_n < t_n+1, we have
+
+   <p>P(X_n+1 = x_n+1 | X_n = x_n, X_n-1 = x_n-1, ..., X_0 = x_0) = P(X_n+1 = x_n+1 | X_n = x_n)
+
+   <p>A continuous-time Markov chain is defined according to an
+<em>infinitesimal generator matrix</em> \bf Q = [Q_i,j],
+where for each i \neq j, Q_i, j is the transition rate
+from state i to state j. The matrix \bf Q must
+satisfy the property that, for all i, \sum_j=1^N Q_i,
+j = 0.
+
+   <p><a name="doc_002dctmcchkQ"></a>
+
+<div class="defun">
+— Function File: [<var>result</var> <var>err</var>] = <b>ctmcchkQ</b> (<var>Q</var>)<var><a name="index-ctmcchkQ-44"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-45"></a>
+If <var>Q</var> is a valid infinitesimal generator matrix, return
+the size (number of rows or columns) of <var>Q</var>. If <var>Q</var> is not
+an infinitesimal generator matrix, set <var>result</var> to zero, and
+<var>err</var> to an appropriate error string.
+
+        </blockquote></div>
+
+<ul class="menu">
+<li><a accesskey="1" href="#State-occupancy-probabilities-_0028CTMC_0029">State occupancy probabilities (CTMC)</a>
+<li><a accesskey="2" href="#Birth_002ddeath-process-_0028CTMC_0029">Birth-death process (CTMC)</a>
+<li><a accesskey="3" href="#Expected-sojourn-times-_0028CTMC_0029">Expected sojourn times (CTMC)</a>
+<li><a accesskey="4" href="#Time_002daveraged-expected-sojourn-times-_0028CTMC_0029">Time-averaged expected sojourn times (CTMC)</a>
+<li><a accesskey="5" href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a>
+<li><a accesskey="6" href="#First-passage-times-_0028CTMC_0029">First passage times (CTMC)</a>
+</ul>
+
+<div class="node">
+<a name="State-occupancy-probabilities-(CTMC)"></a>
+<a name="State-occupancy-probabilities-_0028CTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Birth_002ddeath-process-_0028CTMC_0029">Birth-death process (CTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.2.1 State occupancy probabilities</h4>
+
+<p>Similarly to the discrete case, we denote with \bf \pi(t) =
+(\pi_1(t), \pi_2(t), <small class="dots">...</small>, \pi_N(t) ) the <em>state occupancy
+probability vector</em> at time t. \pi_i(t) is the
+probability that the system is in state i at time t
+≥ 0.
+
+   <p>Given the infinitesimal generator matrix \bf Q and the initial
+state occupancy probabilities \bf \pi(0) = (\pi_1(0),
+\pi_2(0), <small class="dots">...</small>, \pi_N(0)), the state occupancy probabilities
+\bf \pi(t) at time t can be computed as:
+
+<pre class="example">     \pi(t) = \pi(0) exp(Qt)
+</pre>
+   <p class="noindent">where \exp( \bf Q t ) is the matrix exponential
+of \bf Q t. Under certain conditions, there exists a
+<em>stationary state occupancy probability</em> \bf \pi =
+\lim_t \rightarrow +\infty \bf \pi(t), which is independent from
+\bf \pi(0).  \bf \pi is the solution of the following
+linear system:
+
+<pre class="example">     /
+     | \pi Q   = 0
+     | \pi 1^T = 1
+     \
+</pre>
+   <p><a name="doc_002dctmc"></a>
+
+<div class="defun">
+— Function File: <var>p</var> = <b>ctmc</b> (<var>Q</var>)<var><a name="index-ctmc-46"></a></var><br>
+— Function File: <var>p</var> = <b>ctmc</b> (<var>Q, t. p0</var>)<var><a name="index-ctmc-47"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-48"></a><a name="index-continuous-time-Markov-chain-49"></a><a name="index-Markov-chain_002c-state-occupancy-probabilities-50"></a><a name="index-stationary-probabilities-51"></a><a name="index-CTMC-52"></a>
+Compute stationary or transient state occupancy probabilities for a continuous-time Markov chain.
+
+        <p>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 state space {1, 2, <small class="dots">...</small>,
+N} and N \times N infinitesimal generator matrix <var>Q</var>. 
+With three arguments, compute the state occupancy probabilities
+<var>p</var>(1), <small class="dots">...</small>, <var>p</var>(N) that the system is in state i
+at time <var>t</var>, given initial state occupancy probabilities
+<var>p0</var>(1), <small class="dots">...</small>, <var>p0</var>(N) 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 ≤ i \neq j ≤ N. 
+#varQ must satisfy the property that \sum_j=1^N Q_i, j =
+0
+
+          <br><dt><var>t</var><dd>Time at which to compute the transient probability (t ≥
+0). If omitted, the function computes the steady state occupancy
+probability vector.
+
+          <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. 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>p0</var>(1), <small class="dots">...</small>, <var>p0</var>(N).
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> dtmc.
+
+        </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>
+         ⇒ q = 0.50000   0.50000
+</pre>
+   <div class="node">
+<a name="Birth-death-process-(CTMC)"></a>
+<a name="Birth_002ddeath-process-_0028CTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Expected-sojourn-times-_0028CTMC_0029">Expected sojourn times (CTMC)</a>,
+Previous: <a rel="previous" accesskey="p" href="#State-occupancy-probabilities-_0028CTMC_0029">State occupancy probabilities (CTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.2.2 Birth-Death Process</h4>
+
+<p><a name="doc_002dctmcbd"></a>
+
+<div class="defun">
+— Function File: <var>Q</var> = <b>ctmcbd</b> (<var>b, d</var>)<var><a name="index-ctmcbd-53"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-54"></a><a name="index-continuous-time-Markov-chain-55"></a><a name="index-CTMC-56"></a><a name="index-birth_002ddeath-process_002c-CTMC-57"></a>
+Returns the infinitesimal generator matrix Q for a continuous
+birth-death process over state space 1, 2, <small class="dots">...</small>, N. 
+<var>b</var><code>(i)</code> is the transition rate from state i to
+i+1, and <var>d</var><code>(i)</code> is the transition rate from state
+i+1 to state i, i=1, 2, <small class="dots">...</small>, N-1.
+
+        <p>Matrix \bf Q is therefore defined as:
+
+     <pre class="example">          /                                                          \
+          | -b(1)     b(1)                                           |
+          |  d(1) -(d(1)+b(2))     b(2)                              |
+          |           d(2)     -(d(2)+b(3))        b(3)              |
+          |                                                          |
+          |                ...           ...          ...            |
+          |                                                          |
+          |                       d(N-2)    -(d(N-2)+b(N-1))  b(N-1) |
+          |                                       d(N-1)     -d(N-1) |
+          \                                                          /
+</pre>
+        <p class="noindent">where \lambda_i and \mu_i are the birth and
+death rates, respectively.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> dtmcbd.
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Expected-sojourn-times-(CTMC)"></a>
+<a name="Expected-sojourn-times-_0028CTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Time_002daveraged-expected-sojourn-times-_0028CTMC_0029">Time-averaged expected sojourn times (CTMC)</a>,
+Previous: <a rel="previous" accesskey="p" href="#Birth_002ddeath-process-_0028CTMC_0029">Birth-death process (CTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.2.3 Expected Sojourn Times</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). \bf L(t) can be expressed as the
+solution of the following differential equation:
+
+<pre class="example">      dL
+      --(t) = L(t) Q + pi(0),    L(0) = 0
+      dt
+</pre>
+   <p>Alternatively, \bf L(t) can also be expressed in integral
+form as:
+
+<pre class="example">            / t
+     L(t) = |   pi(u) du
+            / 0
+</pre>
+   <p class="noindent">where \bf \pi(t) = \bf \pi(0) \exp(\bf Qt) is
+the state occupancy probability at time t; \exp(\bf Qt)
+is the matrix exponential of \bf Qt.
+
+   <p>If there are absorbing states, we can define the vector <em>expected
+sojourn times until absorption</em> \bf L(\infty), where for each
+transient state i, L_i(\infty) is the expected total
+time spent in state i until absorption, assuming that the
+system started with a given state occupancy probability vector
+\bf \pi(0). Let \tau be the set of transient (i.e.,
+non absorbing) states; let \bf Q_\tau be the restriction of
+\bf Q to the transient substates only. Similarly, let
+\bf \pi_\tau(0) be the restriction of the initial probability
+vector \bf \pi(0) to transient states \tau.
+
+   <p>The expected time to absorption \bf L_\tau(\infty) is defined as
+the solution of the following equation:
+
+<pre class="example">     L_T( inf ) Q_T = -pi_T(0)
+</pre>
+   <p><a name="doc_002dctmcexps"></a>
+
+<div class="defun">
+— Function File: <var>L</var> = <b>ctmcexps</b> (<var>Q, t, p </var>)<var><a name="index-ctmcexps-58"></a></var><br>
+— Function File: <var>L</var> = <b>ctmcexps</b> (<var>Q, p</var>)<var><a name="index-ctmcexps-59"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-60"></a><a name="index-expected-sojourn-time_002c-CTMC-61"></a>
+With three arguments, compute the expected times <var>L</var><code>(i)</code>
+spent in each state i during the time interval [0,t],
+assuming that the initial occupancy vector is <var>p</var>. With two
+arguments, compute the expected time <var>L</var><code>(i)</code> spent in each
+transient state i until absorption.
+
+        <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, 1
+≤ i \neq j ≤ N. The matrix <var>Q</var> must also satisfy the
+condition \sum_j=1^N Q_ij = 0.
+
+          <br><dt><var>t</var><dd>If given, compute the expected sojourn times in [0,t]
+
+          <br><dt><var>p</var><dd>Initial occupancy probability vector; <var>p</var><code>(i)</code> is the
+probability the system is in state i at time 0, i = 1,
+<small class="dots">...</small>, N
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>L</var><dd>If this function is called with three arguments, <var>L</var><code>(i)</code> is
+the expected time spent in state i during the interval
+[0,t]. If this function is called with two arguments
+<var>L</var><code>(i)</code> is the expected time spent in transient state
+i until absorption; if state i is absorbing,
+<var>L</var><code>(i)</code> is zero.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> dtmcexps.
+
+        </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 \bf \pi_0=(1,0,0,0).
+
+<pre class="example"><pre class="verbatim">      lambda = 0.5;
+      N = 4;
+      b = lambda*[1:N-1];
+      d = zeros(size(b));
+      Q = ctmcbd(b,d);
+      t = linspace(0,10,100);
+      p0 = zeros(1,N); p0(1)=1;
+      L = zeros(length(t),N);
+      for i=1:length(t)
+        L(i,:) = ctmcexps(Q,t(i),p0);
+      endfor
+      plot( t, L(:,1), ";State 1;", "linewidth", 2, ...
+            t, L(:,2), ";State 2;", "linewidth", 2, ...
+            t, L(:,3), ";State 3;", "linewidth", 2, ...
+            t, L(:,4), ";State 4;", "linewidth", 2 );
+      legend("location","northwest");
+      xlabel("Time");
+      ylabel("Expected sojourn time");
+</pre>
+</pre>
+   <div class="node">
+<a name="Time-averaged-expected-sojourn-times-(CTMC)"></a>
+<a name="Time_002daveraged-expected-sojourn-times-_0028CTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a>,
+Previous: <a rel="previous" accesskey="p" href="#Expected-sojourn-times-_0028CTMC_0029">Expected sojourn times (CTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.2.4 Time-Averaged Expected Sojourn Times</h4>
+
+<p><a name="doc_002dctmctaexps"></a>
+
+<div class="defun">
+— Function File: <var>M</var> = <b>ctmctaexps</b> (<var>Q, t, p</var>)<var><a name="index-ctmctaexps-62"></a></var><br>
+— Function File: <var>M</var> = <b>ctmctaexps</b> (<var>Q, p</var>)<var><a name="index-ctmctaexps-63"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-64"></a><a name="index-time_002dalveraged-sojourn-time_002c-CTMC-65"></a><a name="index-continuous-time-Markov-chain-66"></a><a name="index-CTMC-67"></a>
+Compute the <em>time-averaged sojourn time</em> <var>M</var><code>(i)</code>,
+defined as the fraction of the time interval [0,t] (or until
+absorption) spent in state i, assuming that the state
+occupancy probabilities at time 0 are <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 ≤ i \neq j ≤ N. The
+matrix <var>Q</var> must also satisfy the condition \sum_j=1^N Q_ij = 0
+
+          <br><dt><var>t</var><dd>Time. If omitted, the results are computed until absorption.
+
+          <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>When called with three arguments, <var>M</var><code>(i)</code> is the expected
+fraction of the interval [0,t] spent in state i
+assuming that the state occupancy probability at time zero is
+<var>p</var>. When called with two arguments, <var>M</var><code>(i)</code> is the
+expected fraction of time until absorption spent in state i;
+in this case the mean time to absorption is <code>sum(</code><var>M</var><code>)</code>.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> dtmctaexps.
+
+        </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-5,30,100);
+      p = zeros(1,N); p(1)=1;
+      M = zeros(length(t),N);
+      for i=1:length(t)
+        M(i,:) = ctmctaexps(Q,t(i),p);
+      endfor
+      clf;
+      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="Mean-time-to-absorption-(CTMC)"></a>
+<a name="Mean-time-to-absorption-_0028CTMC_0029"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#First-passage-times-_0028CTMC_0029">First passage times (CTMC)</a>,
+Previous: <a rel="previous" accesskey="p" href="#Time_002daveraged-expected-sojourn-times-_0028CTMC_0029">Time-averaged expected sojourn times (CTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.2.5 Mean Time to Absorption</h4>
+
+<p><a name="doc_002dctmcmtta"></a>
+
+<div class="defun">
+— Function File: <var>t</var> = <b>ctmcmtta</b> (<var>Q, p</var>)<var><a name="index-ctmcmtta-68"></a></var><br>
+<blockquote>
+        <p><a name="index-Markov-chain_002c-continuous-time-69"></a><a name="index-continuous-time-Markov-chain-70"></a><a name="index-CTMC-71"></a><a name="index-mean-time-to-absorption_002c-CTMC-72"></a>
+Compute the Mean-Time to Absorption (MTTA) of the CTMC described by
+the infinitesimal generator matrix <var>Q</var>, starting from initial
+occupancy probabilities <var>p</var>. 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>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> dtmcmtta.
+
+        </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;
+      birth = 0*death;
+      Q = ctmcbd(birth,death);
+      t = ctmcmtta(Q,[0 0 0 1])
+</pre>
+         ⇒ 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-73"></a><a name="index-Greiner_002c-S_002e-74"></a><a name="index-de-Meer_002c-H_002e-75"></a><a name="index-Trivedi_002c-K_002e-76"></a>
+<div class="node">
+<a name="First-passage-times-(CTMC)"></a>
+<a name="First-passage-times-_0028CTMC_0029"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a>,
+Up: <a rel="up" accesskey="u" href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a>
+
+</div>
+
+<h4 class="subsection">3.2.6 First Passage Times</h4>
+
+<p><a name="doc_002dctmcfpt"></a>
+
+<div class="defun">
+— Function File: <var>M</var> = <b>ctmcfpt</b> (<var>Q</var>)<var><a name="index-ctmcfpt-77"></a></var><br>
+— Function File: <var>m</var> = <b>ctmcfpt</b> (<var>Q, i, j</var>)<var><a name="index-ctmcfpt-78"></a></var><br>
+<blockquote>
+        <p><a name="index-first-passage-times_002c-CTMC-79"></a><a name="index-CTMC-80"></a><a name="index-continuous-time-Markov-chain-81"></a><a name="index-Markov-chain_002c-continuous-time-82"></a>
+Compute mean first passage times for an irreducible continuous-time
+Markov chain.
+
+        <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 ≤ i \neq j ≤ 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.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>M</var><dd><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>. 
+We set <var>M</var><code>(i,i) = 0</code>.
+
+          <br><dt><var>m</var><dd><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>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> dtmcfpt.
+
+        </blockquote></div>
+
+<!-- This file has been automatically generated from singlestation.txi -->
+<!-- by proc.m. Do not edit this file, all changes will be lost -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014 Moreno Marzolla -->
+<!-- This file is part of the queueing package. -->
+<!-- The queueing package 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 package 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 package; 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: <a rel="next" accesskey="n" href="#Queueing-Networks">Queueing Networks</a>,
+Previous: <a rel="previous" accesskey="p" href="#Markov-Chains">Markov Chains</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">4 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-Erlang_002dB-Formula">The Erlang-B Formula</a>
+<li><a accesskey="4" href="#The-Erlang_002dC-Formula">The Erlang-C Formula</a>
+<li><a accesskey="5" href="#The-Engset-Formula">The Engset Formula</a>
+<li><a accesskey="6" href="#The-M_002fM_002finf-System">The M/M/inf System</a>:   Infinite-server (delay center) station. 
+<li><a accesskey="7" href="#The-M_002fM_002f1_002fK-System">The M/M/1/K System</a>:   Single-server, finite-capacity queueing station. 
+<li><a accesskey="8" href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a>:   Multiple-server, finite-capacity queueing station. 
+<li><a accesskey="9" href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a>:   Asymmetric multiple-server queueing station. 
+<li><a href="#The-M_002fG_002f1-System">The M/G/1 System</a>:  Single-server with general service time distribution. 
+<li><a href="#The-M_002fHm_002f1-System">The M/Hm/1 System</a>:  Single-server with hyperexponential service time distribution. 
+</ul>
+
+<!-- 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: <a rel="next" accesskey="n" href="#The-M_002fM_002fm-System">The M/M/m System</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.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. Requests arrive according to a Poisson process
+with rate \lambda; the service time is exponentially
+distributed with average service rate \mu. The system is stable
+if \lambda < \mu.
+
+   <p><a name="doc_002dqsmm1"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>] = <b>qsmm1</b> (<var>lambda, mu</var>)<var><a name="index-qsmm1-83"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fM_002f1_007d-system-84"></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> ≥ 0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code> > </code><var>lambda</var>).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd>Server utilization
+
+          <br><dt><var>R</var><dd>Server response time
+
+          <br><dt><var>Q</var><dd>Average number of requests in the system
+
+          <br><dt><var>X</var><dd>Server throughput. If the system is ergodic (<var>mu</var><code> >
+</code><var>lambda</var>), we 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> qsmmm, qsmminf, qsmmmk.
+
+        </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-85"></a><a name="index-Greiner_002c-S_002e-86"></a><a name="index-de-Meer_002c-H_002e-87"></a><a name="index-Trivedi_002c-K_002e-88"></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: <a rel="next" accesskey="n" href="#The-Erlang_002dB-Formula">The Erlang-B Formula</a>,
+Previous: <a rel="previous" accesskey="p" href="#The-M_002fM_002f1-System">The M/M/1 System</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.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 shared
+FCFS 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 requests in the system:
+
+<pre class="example">     mu(n) = min(m,n)*mu
+</pre>
+   <p class="noindent">where \mu is the service rate of each individual server.
+
+   <p><a name="doc_002dqsmmm"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>, <var>pm</var>] = <b>qsmmm</b> (<var>lambda, mu</var>)<var><a name="index-qsmmm-89"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>, <var>pm</var>] = <b>qsmmm</b> (<var>lambda, mu, m</var>)<var><a name="index-qsmmm-90"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fM_002fm_007d-system-91"></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 servers connected to a single FCFS
+queue.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate (<var>lambda</var><code>>0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code>></code><var>lambda</var>).
+
+          <br><dt><var>m</var><dd>Number of servers (<var>m</var><code> ≥ 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> erlangc,qsmm1,qsmminf,qsmmmk.
+
+        </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-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>
+<!-- Erlang-B -->
+<div class="node">
+<a name="The-Erlang-B-Formula"></a>
+<a name="The-Erlang_002dB-Formula"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#The-Erlang_002dC-Formula">The Erlang-C Formula</a>,
+Previous: <a rel="previous" accesskey="p" href="#The-M_002fM_002fm-System">The M/M/m System</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.3 The Erlang-B Formula</h3>
+
+<p><a name="doc_002derlangb"></a>
+
+<div class="defun">
+— Function File: <var>B</var> = <b>erlangb</b> (<var>A, m</var>)<var><a name="index-erlangb-96"></a></var><br>
+<blockquote>
+        <p><a name="index-Erlang_002dB-formula-97"></a>
+Compute the value of the Erlang-B formula E_B(A, m) giving the
+probability that an open system with m identical servers,
+arrival rate \lambda, individual service rate \mu
+and offered load A = \lambda / \mu has all servers busy.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>A</var><dd>Offered load, defined as A = \lambda / \mu where
+\lambda is the mean arrival rate and \mu the mean
+service rate of each individual server (real, A > 0).
+
+          <br><dt><var>m</var><dd>Number of identical servers (integer, m ≥ 1). Default m = 1
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>B</var><dd>The value E_B(A, m)
+
+        </dl>
+
+        <p><var>A</var> or <var>m</var> can be vectors, and in this case, the results will
+be vectors as well.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qsmmm.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">G. Zeng, <cite>Two common properties of the erlang-B function, erlang-C function, and Engset blocking function</cite>, Mathematical and Computer Modelling, Volume 37, Issues 12-13, June 2003, Pages 1287-1296
+
+   <p><a name="index-Zeng_002c-G_002e-98"></a>
+<!-- Erlang-c -->
+<div class="node">
+<a name="The-Erlang-C-Formula"></a>
+<a name="The-Erlang_002dC-Formula"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#The-Engset-Formula">The Engset Formula</a>,
+Previous: <a rel="previous" accesskey="p" href="#The-Erlang_002dB-Formula">The Erlang-B Formula</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.4 The Erlang-C Formula</h3>
+
+<p><a name="doc_002derlangc"></a>
+
+<div class="defun">
+— Function File: <var>C</var> = <b>erlangc</b> (<var>A, m</var>)<var><a name="index-erlangc-99"></a></var><br>
+<blockquote>
+        <p><a name="index-Erlang_002dC-formula-100"></a>
+Compute the steady-state probability E_C(A, m) that an open
+queueing system with m identical servers, infinite wating
+space, arrival rate \lambda, individual service rate
+\mu and offered load A = \lambda / \mu has all the
+servers busy.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>A Offered load. A = \lambda / \mu where</var><dd>\lambda is the mean arrival rate and \mu the mean
+service rate of each individual server (real, 0 < A < m).
+
+          <br><dt><var>m Number of identical servers (integer, m ≥ 1).</var><dd>Default m = 1
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>B The value E_C(A, m)</var><dd>
+</dl>
+
+        <p><var>A</var> or <var>m</var> can be vectors, and in this case, the results will
+be vectors as well.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qsmmm.
+
+        </blockquote></div>
+
+<!-- Engset -->
+<div class="node">
+<a name="The-Engset-Formula"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#The-M_002fM_002finf-System">The M/M/inf System</a>,
+Previous: <a rel="previous" accesskey="p" href="#The-Erlang_002dC-Formula">The Erlang-C Formula</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.5 The Engset Formula</h3>
+
+<p><a name="doc_002dengset"></a>
+
+<div class="defun">
+— Function File: <var>B</var> = <b>engset</b> (<var>A, m, n</var>)<var><a name="index-engset-101"></a></var><br>
+<blockquote>
+        <p><a name="index-Engset-loss-formula-102"></a>
+Compute the Engset blocking probability P_b(A, m, n) for a system
+with a finite population of n users, m identical
+servers, no queue, individual service rate \mu, individual
+arrival rate \lambda (i.e., the time until a user tries to
+request service is exponentially distributed with mean 1 /
+\lambda), and offered load A = \lambda / \mu.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>A</var><dd>Offered load, defined as A = \lambda / \mu where
+\lambda is the mean arrival rate and \mu the mean
+service rate of each individual server (real, A > 0).
+
+          <br><dt><var>m</var><dd>Number of identical servers (integer, m ≥ 1). Default m = 1
+
+          <br><dt><var>n</var><dd>Number of requests (integer, n ≥ 1). Default n = 1
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>B</var><dd>The value P_b(A, m, n)
+
+        </dl>
+
+        <p><var>A</var>, <var>m</var> or n can be vectors, and in this case, the
+results will be vectors as well.
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> erlangb, erlangc.
+
+        </blockquote></div>
+
+<!-- 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: <a rel="next" accesskey="n" href="#The-M_002fM_002f1_002fK-System">The M/M/1/K System</a>,
+Previous: <a rel="previous" accesskey="p" href="#The-Engset-Formula">The Engset Formula</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.6 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_002dqsmminf"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>] = <b>qsmminf</b> (<var>lambda, mu</var>)<var><a name="index-qsmminf-103"></a></var><br>
+<blockquote>
+        <p>Compute utilization, response time, average number of requests and throughput for a M/M/\infty queue.
+
+        <p>The M/M/\infty system has an infinite number of identical
+servers; this kind of system is always stable for every arrival and
+service rates.
+
+        <p><a name="index-g_t_0040math_007bM_002fM_002f_007dinf-system-104"></a>
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate (<var>lambda</var><code>>0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code>>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-105"></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> qsmm1,qsmmm,qsmmmk.
+
+        </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-106"></a><a name="index-Greiner_002c-S_002e-107"></a><a name="index-de-Meer_002c-H_002e-108"></a><a name="index-Trivedi_002c-K_002e-109"></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: <a rel="next" accesskey="n" href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a>,
+Previous: <a rel="previous" accesskey="p" href="#The-M_002fM_002finf-System">The M/M/inf System</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.7 The M/M/1/K System</h3>
+
+<p>In a M/M/1/K finite capacity system there is a single server
+and there can be at most k \geq 1 jobs at any time (including
+the job currently in service). 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_002dqsmm1k"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>, <var>pK</var>] = <b>qsmm1k</b> (<var>lambda, mu, K</var>)<var><a name="index-qsmm1k-110"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fM_002f1_002fK_007d-system-111"></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>>0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code>>0</code>).
+
+          <br><dt><var>K</var><dd>Maximum number of requests allowed in the system (<var>K</var><code> ≥ 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> qsmm1,qsmminf,qsmmm.
+
+        </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: <a rel="next" accesskey="n" href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a>,
+Previous: <a rel="previous" accesskey="p" href="#The-M_002fM_002f1_002fK-System">The M/M/1/K System</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.8 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_002dqsmmmk"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>, <var>pK</var>] = <b>qsmmmk</b> (<var>lambda, mu, m, K</var>)<var><a name="index-qsmmmk-112"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fM_002fm_002fK_007d-system-113"></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 ≥ 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>>0</code>).
+
+          <br><dt><var>mu</var><dd>Service rate (<var>mu</var><code>>0</code>).
+
+          <br><dt><var>m</var><dd>Number of servers (<var>m</var><code> ≥ 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> ≥ </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> qsmm1,qsmminf,qsmmm.
+
+        </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-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>
+<!-- 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: <a rel="next" accesskey="n" href="#The-M_002fG_002f1-System">The M/G/1 System</a>,
+Previous: <a rel="previous" accesskey="p" href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.9 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_002dqsammm"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qsammm</b> (<var>lambda, mu</var>)<var><a name="index-qsammm-118"></a></var><br>
+<blockquote>
+        <p><a name="index-asymmetric-_0040math_007bM_002fM_002fm_007d-system-119"></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>>0</code>).
+
+          <br><dt><var>mu</var><dd><var>mu</var><code>(i)</code> is the service rate of server
+i, 1 ≤ i ≤ m. 
+The system must be ergodic (<var>lambda</var><code> < 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> qsmmm.
+
+        </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-120"></a><a name="index-Greiner_002c-S_002e-121"></a><a name="index-de-Meer_002c-H_002e-122"></a><a name="index-Trivedi_002c-K_002e-123"></a>
+<div class="node">
+<a name="The-M%2fG%2f1-System"></a>
+<a name="The-M_002fG_002f1-System"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#The-M_002fHm_002f1-System">The M/Hm/1 System</a>,
+Previous: <a rel="previous" accesskey="p" href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.10 The M/G/1 System</h3>
+
+<p><a name="doc_002dqsmg1"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>] = <b>qsmg1</b> (<var>lambda, xavg, x2nd</var>)<var><a name="index-qsmg1-124"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fG_002f1_007d-system-125"></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> qsmh1.
+
+        </blockquote></div>
+
+<div class="node">
+<a name="The-M%2fHm%2f1-System"></a>
+<a name="The-M_002fHm_002f1-System"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#The-M_002fG_002f1-System">The M/G/1 System</a>,
+Up: <a rel="up" accesskey="u" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>
+
+</div>
+
+<h3 class="section">4.11 The M/H_m/1 System</h3>
+
+<p><a name="doc_002dqsmh1"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>p0</var>] = <b>qsmh1</b> (<var>lambda, mu, alpha</var>)<var><a name="index-qsmh1-126"></a></var><br>
+<blockquote>
+        <p><a name="index-g_t_0040math_007bM_002fH_005fm_002f1_007d-system-127"></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) =  >  alpha(j) * (1-exp(-mu(j)*x))   x>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>
+
+        </blockquote></div>
+
+<!-- This file has been automatically generated from queueingnetworks.txi -->
+<!-- by proc.m. Do not edit this file, all changes will be lost -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014 Moreno Marzolla -->
+<!-- This file is part of the queueing package. -->
+<!-- The queueing package 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 package 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 package; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="Queueing-Networks"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#References">References</a>,
+Previous: <a rel="previous" accesskey="p" href="#Single-Station-Queueing-Systems">Single Station Queueing Systems</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">5 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="#Single-Class-Models">Single Class Models</a>:              Queueing Models with a single job class
+<li><a accesskey="3" href="#Multiple-Class-Models">Multiple Class Models</a>:            Queueing Models with multiple job classess
+<li><a accesskey="4" href="#Generic-Algorithms">Generic Algorithms</a>:               High-level functions for QN analysis
+<li><a accesskey="5" href="#Bounds-Analysis">Bounds Analysis</a>:                  Computation of asymptotic performance bounds
+<li><a accesskey="6" href="#QN-Analysis-Examples">QN Analysis Examples</a>:             Queueing Networks Analysis examples
+</ul>
+
+<p><a name="index-queueing-networks-128"></a>
+<!-- INTRODUCTION -->
+<div class="node">
+<a name="Introduction-to-QNs"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Single-Class-Models">Single Class Models</a>,
+Up: <a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">5.1 Introduction to QNs</h3>
+
+<p>Queueing Networks (QN) are a very simple yet powerful modeling tool
+which can be used to analyze many kind of systems. In its simplest
+form, a QN is made of K service centers. Each center k
+has a queue, which is connected to m_k (generally identical)
+<em>servers</em>. Arriving customers (requests) join the queue if there
+is a slot available. Then, requests are served according to a
+(de)queueing policy (e.g., FIFO). After service completes, requests
+leave the server and can join another queue or exit from the system.
+
+   <p>Service centers for which m_k = \infty are called <em>delay
+centers</em> or <em>infinite servers</em>. In this kind of centers, every
+request always finds one available server, so queueing never occurs.
+
+   <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 (for which m_k = \infty).
+
+   </dl>
+
+   <p>Queueing models can be <em>open</em> or <em>closed</em>. In open models
+there is an infinite population of requests; new customers are
+generated outside the system, and eventually leave the system. In
+closed systems there is a fixed population of request.
+
+   <p>Queueing models can have a single request class (<em>single class
+models</em>), meaning that all requests behave in the same way (e.g., they
+spend the same average time on each particular server). In
+<em>multiple class models</em> there are multiple request classes, each
+one with its own parameters. Furthermore, in multiclass models there
+can be open and closed classes of requests within the same system.
+
+   <p>A particular class of QN models, <em>product-form</em> QNs, is of
+particular interest. Product-form 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>
+
+   <p>Product-form networks are attractive because they be efficiently
+analyzed to compute steady-state performance measures.
+
+<!-- Single Class Models -->
+<div class="node">
+<a name="Single-Class-Models"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Multiple-Class-Models">Multiple Class Models</a>,
+Previous: <a rel="previous" accesskey="p" href="#Introduction-to-QNs">Introduction to QNs</a>,
+Up: <a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">5.2 Single Class Models</h3>
+
+<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_k<dd>(Open models only) External arrival rate to service center k.
+
+     <br><dt>lambda<dd>(Open models only) Overall external arrival rate to the system as a whole: \lambda =
+\sum_k \lambda_k.
+
+     <br><dt>N<dd>(Closed models only) Total number of requests in the system.
+
+     <br><dt>S_k<dd>Average service time. S_k is the average service time on service
+center k. In other words, S_k 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_i, j<dd>Routing probability matrix. \bf P = [P_i, j] is a K
+\times K matrix such that P_i, j 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_i,
+j.
+
+     <br><dt>V_k<dd>Mean number of visits the center k (also called <em>visit
+ratio</em> or <em>relative arrival rate</em>).
+
+   </dl>
+
+<p class="noindent"><strong>Model Outputs</strong>
+
+     <dl>
+<dt>U_k<dd>Service center utilization. U_k is the utilization of service
+center k. The utilization is defined as the fraction of time in
+which the resource is busy (i.e., the server is processing requests). 
+If center k is a single-server or multiserver node, then
+0 ≤ U_k ≤ 1. If center k is an infinite server
+node (delay center), then U_k denotes the <em>traffic
+intensity</em> and is defined as U_k = X_k S_k; in this case the
+utilization may be greater than one.
+
+     <br><dt>R_k<dd>Average response time. R_k is the average response time of
+service center k. 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_k<dd>Average number of customers. Q_k is the average number of
+requests in service center k. This includes both the requests in
+the queue, and the request being served.
+
+     <br><dt>X_k<dd>Throughput. X_k is the throughput of service center k. 
+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_k / V_k for any k for
+which V_k \neq 0
+
+     <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 = \sum_k=1 Q_k; for
+closed systems, this can be written as 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 V_j satisfy the following equation:
+
+<pre class="example">                       K
+                      ___
+                     \
+     V_j = P_(0, j) + >   V_i P_(i, j)    j=1,...,K
+                     /___
+                      i=1
+</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">     /
+     |         K
+     |        ___
+     |       \
+     | V_j =  >   V_i P_(i, j)     j=1,...,K
+     |       /___
+     |        i=1
+     |
+     | V_r = 1                     for a selected reference station r
+     \
+</pre>
+   <p>Note that the set of traffic equations V_j = \sum_i=1^K V_i
+P_i, j alone can only be solved up to a multiplicative constant; to
+get a unique solution we impose an additional constraint V_r =
+1. This constraint is equivalent to defining station r as the
+<em>reference station</em>; the default is r=1,
+see <a href="#doc_002dqncsvisits">doc-qncsvisits</a>. A job that returns to the reference station is
+assumed to have completed its activity cycle. The network throughput
+is set to the throughput of the reference station.
+
+   <p><a name="doc_002dqncsvisits"></a>
+
+<div class="defun">
+— Function File: <var>V</var> = <b>qncsvisits</b> (<var>P</var>)<var><a name="index-qncsvisits-129"></a></var><br>
+— Function File: <var>V</var> = <b>qncsvisits</b> (<var>P, r</var>)<var><a name="index-qncsvisits-130"></a></var><br>
+<blockquote>
+        <p>Compute the mean number of visits to the service centers of a
+single class, closed network with K service centers.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd><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 must be strongly connected, meaning that each node must be
+reachable from every other node.
+
+          <br><dt><var>r</var><dd>Index of the reference station, r \in {1, <small class="dots">...</small>, K};
+Default <var>r</var><code>=1</code>. The traffic equations are solved by
+imposing the condition <var>V</var><code>(r) = 1</code>. A request returning to
+the reference station completes its activity cycle.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>V</var><dd><var>V</var><code>(i)</code> is the average number of visits to service center
+i, assuming center r as the reference station.
+
+        </dl>
+
+        </blockquote></div>
+
+   <p><a name="doc_002dqnosvisits"></a>
+
+<div class="defun">
+— Function File: <var>V</var> = <b>qnosvisits</b> (<var>P, lambda</var>)<var><a name="index-qnosvisits-131"></a></var><br>
+<blockquote>
+        <p>Compute the average number of visits to the service centers of a single
+class open Queueing Network with K service centers.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd><var>P</var><code>(i,j)</code> is the probability that a request which completed
+service at center i is routed to center j.
+
+          <br><dt><var>lambda</var><dd><var>lambda</var><code>(i)</code> is the external arrival rate to
+center i.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>V</var><dd><var>V</var><code>(i)</code> is the average number of
+visits to server i.
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>EXAMPLE</strong>
+
+   <div class="float">
+<a name="fig_003aqn_005fclosed_005fsingle"></a><div align="center"><img src="qn_closed_single.png" alt="qn_closed_single.png"></div>
+   <p><strong class="float-caption">Figure 5.1: Closed network with a single class of requests</strong></p></div>
+
+   <p><a href="#fig_003aqn_005fclosed_005fsingle">Figure 5.1</a> shows a closed QN with a single class of
+requests. The network has three service centers, labeled <em>CPU</em>,
+<em>Disk1</em>, <em>Disk2</em>. and represents the so called <em>central
+server</em> model of a computer system. Requests spend some time at the
+CPU, which is represented by a PS (Processor Sharing) node. After
+that, requests are routed to node Disk1 with probability 0.3,
+and to node Disk2 with probability 0.7. Both Disk1 and Disk2
+are modeled as FCFS nodes.
+
+   <p>If we enumerate the servers as CPU=1, Disk1=2, Disk2=3, we can define
+the routing matrix as follows:
+
+<pre class="example">         / 0  0.3  0.7 \
+     P = | 1  0    0   |
+         \ 1  0    0   /
+</pre>
+   <p>The visit ratios V using station 1 as the reference
+station can be computed with:
+
+<pre class="example"><pre class="verbatim">      P = [0 0.3 0.7; ...
+           1 0   0  ; ...
+           1 0   0  ];
+      V = qncsvisits(P)
+</pre>
+        ⇒ V = 1.00000   0.30000   0.70000
+</pre>
+   <p class="noindent"><strong>EXAMPLE</strong>
+
+   <div class="float">
+<a name="fig_003aqn_005fopen_005fsingle"></a><div align="center"><img src="qn_open_single.png" alt="qn_open_single.png"></div>
+   <p><strong class="float-caption">Figure 5.2: Open Queueing Network with a single class of requests</strong></p></div>
+
+   <p><a href="#fig_003aqn_005fopen_005fsingle">Figure 5.2</a> shows a open QN with a single class of
+requests. The network has the same structure as the one in
+<a href="#fig_003aqn_005fclosed_005fsingle">Figure 5.1</a>, with the difference that here we have a
+stream of jobs arriving from outside the system, at a rate
+\lambda. After service completion at the CPU, a job can leave
+the system with probability 0.2, or be transferred to other nodes with
+the probabilities shown in the figure.
+
+   <p>The routing matrix of this network is
+
+<pre class="example">         / 0  0.3  0.5 \
+     P = | 1  0    0   |
+         \ 1  0    0   /
+</pre>
+   <p>If we let \lambda = 1.2, we can compute the visit ratios
+V as follows:
+
+<pre class="example"><pre class="verbatim">      p = 0.3;
+      lambda = 1.2
+      P = [0 0.3 0.5; ...
+           1 0   0  ; ...
+           1 0   0  ];
+      V = qnosvisits(P,[1.2 0 0])
+</pre>
+        ⇒ V = 5.0000   1.5000   2.5000
+</pre>
+   <p>Function <samp><span class="command">qnosvisits</span></samp> expects a vector with K elements
+as a second parameter, for open networks only. The vector contains the
+arrival rates at each individual node; since in our example external
+arrivals exist only for node S_1 with rate \lambda =
+1.2, the second parameter is <code>[1.2, 0, 0]</code>.
+
+<!-- Open Networks -->
+<h4 class="subsection">5.2.1 Open 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_002dqnos"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnos</b> (<var>lambda, S, V</var>)<var><a name="index-qnos-132"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnos</b> (<var>lambda, S, V, m</var>)<var><a name="index-qnos-133"></a></var><br>
+<blockquote>
+        <p><a name="index-open-network_002c-single-class-134"></a><a name="index-BCMP-network-135"></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 k can consist of <var>m</var><code>(k) ≥ 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>>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)>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) ≥ 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) < 1</code>, enter 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 all 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,qnosvisits.
+
+        </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] = qnos( 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 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-136"></a><a name="index-Greiner_002c-S_002e-137"></a><a name="index-de-Meer_002c-H_002e-138"></a><a name="index-Trivedi_002c-K_002e-139"></a>
+<!-- Closed Networks -->
+
+<h4 class="subsection">5.2.2 Closed Networks</h4>
+
+<p><a name="doc_002dqncsmva"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qncsmva</b> (<var>N, S, V</var>)<var><a name="index-qncsmva-140"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qncsmva</b> (<var>N, S, V, m</var>)<var><a name="index-qncsmva-141"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qncsmva</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qncsmva-142"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029-143"></a><a name="index-closed-network_002c-single-class-144"></a><a name="index-normalization-constant-145"></a>
+Analyze closed, single class queueing networks using the exact Mean Value Analysis (MVA) algorithm.
+
+        <p>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>qncsmvald</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> ≥ 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 at center k
+(<var>S</var><code>(k) ≥ 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) ≥ 0</code>).
+
+          <br><dt><var>Z</var><dd>External delay for customers (<var>Z</var><code> ≥ 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) < 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,
+0 ≤ U(k) ≤ 1. If k is an IS node
+(<var>m</var><code>(k) < 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>. In this case the
+value of <var>U</var><code>(k)</code> may be greater than one.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the response time at center k. 
+The <em>Residence Time</em> at center k is
+<var>R</var><code>(k) * </code><var>V</var><code>(k)</code>. 
+The system response time <var>Rsys</var>
+can be computed either as <var>Rsys</var><code> = </code><var>N</var><code>/</code><var>Xsys</var><code> - Z</code>
+or as <var>Rsys</var><code> = dot(</code><var>R</var><code>,</code><var>V</var><code>)</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>
+
+        <blockquote>
+<b>Note on numerical stability:</b> In presence of load-dependent servers (e.g., if <var>m</var><code>(k)>1</code>
+for some k), the MVA algorithm is known to be numerically
+unstable. Generally this problem manifests itself as negative
+response times or utilizations. This is not a problem with the
+<code>queueing</code> toolbox, but with the Mean Value Analysis algorithm,
+and therefore has currently no easy workaround (aoart from using a
+different solution technique, if available). This function prints a
+warning if it detects numerical problems; you can disable the warning
+with the command <code>warning("off", "qn:numerical-instability")</code>. 
+</blockquote>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qncsmvald,qncscmva.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">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–322. <a href="http://doi.acm.org/10.1145/322186.322195">10.1145/322186.322195</a>
+
+   <p><a name="index-Reiser_002c-M_002e-146"></a><a name="index-Lavenberg_002c-S_002e-S_002e-147"></a>
+This implementation is described in R. Jain , <cite>The Art of
+Computer Systems Performance Analysis</cite>, Wiley, 1991, p. 577. 
+Multi-server nodes 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-148"></a><a name="index-Bolch_002c-G_002e-149"></a><a name="index-Greiner_002c-S_002e-150"></a><a name="index-de-Meer_002c-H_002e-151"></a><a name="index-Trivedi_002c-K_002e-152"></a>
+<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] = qncsmva(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>
+   <!-- MVA for single class, closed networks with load dependent servers -->
+   <p><a name="doc_002dqncsmvald"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncsmvald</b> (<var>N, S, V</var>)<var><a name="index-qncsmvald-153"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncsmvald</b> (<var>N, S, V, Z</var>)<var><a name="index-qncsmvald-154"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029-155"></a><a name="index-MVA-156"></a><a name="index-closed-network_002c-single-class-157"></a><a name="index-load_002ddependent-service-center-158"></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>qncsmva</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> ≥ 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 ≤ n
+≤ 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) ≥ 0</code>).
+
+          <br><dt><var>Z</var><dd>external delay ("think time", <var>Z</var><code> ≥ 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>
+<b>Note:</b> In presence of load-dependent servers,
+the MVA algorithm is known to be numerically
+unstable. Generally this problem manifests itself as negative
+response times produced by this function. 
+</blockquote>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">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–322. <a href="http://doi.acm.org/10.1145/322186.322195">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, “Networks with Load-Deèpendent Service: Closed
+Networks”.
+
+   <p><a name="index-Bolch_002c-G_002e-159"></a><a name="index-Greiner_002c-S_002e-160"></a><a name="index-de-Meer_002c-H_002e-161"></a><a name="index-Trivedi_002c-K_002e-162"></a>
+<!-- CMVA for single class, closed networks with a single load dependent servers -->
+<a name="doc_002dqncscmva"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncscmva</b> (<var>N, S, Sld, V</var>)<var><a name="index-qncscmva-163"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncscmva</b> (<var>N, S, Sld, V, Z</var>)<var><a name="index-qncscmva-164"></a></var><br>
+<blockquote>
+        <p><a name="index-conditional-MVA-_0028CMVA_0029-165"></a><a name="index-Mean-Value-Analysis_002c-conditional-_0028CMVA_0029-166"></a><a name="index-closed-network_002c-single-class-167"></a><a name="index-CMVA-168"></a>
+This is the implementation of the original Conditional MVA (CMVA)
+algorithm, a numerically stable variant of MVA, as described in G. 
+Casale, <cite>A Note on Stable Flow-Equivalent Aggregation in Closed
+Networks</cite>. This function supports a network of M ≥ 1
+service centers and a single delay center. Servers 1, <small class="dots">...</small>,
+M-1 are load-independent; server M is load-dependent.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Number of requests in the system, <var>N</var><code> ≥ 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>Vector of mean service times for load-independent (fixed rate) servers. 
+Specifically, <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) > 0</code>). If there are no
+fixed-rate servers, then <code>S = []</code>
+
+          <br><dt><var>Sld</var><dd><var>Sld</var><code>(n)</code> is the inverse service rate at server M
+(the load-dependent server) when there are n requests,
+n=1, <small class="dots">...</small>, N. <var>Sld</var><code>(n) = </code> 1 / \mu(n).
+
+          <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, where <var>V</var><code>(k) ≥ 0</code>. 
+<var>V</var><code>(1:M-1)</code> are the visit rates to the fixed rate servers;
+<var>V</var><code>(M)</code> is the visit rate to the load dependent server.
+
+          <br><dt><var>Z</var><dd>External delay for customers (<var>Z</var><code> ≥ 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 (k=1, <small class="dots">...</small>, M)
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the response time of center k, (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, (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, (k=1, <small class="dots">...</small>, M).
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">G. Casale. <cite>A note on stable flow-equivalent aggregation in
+closed networks</cite>. Queueing Syst. Theory Appl., 60:193–-202, December
+2008, <a href="http://dx.doi.org/10.1007/s11134-008-9093-6">10.1007/s11134-008-9093-6</a>
+
+   <p><a name="index-Casale_002c-G_002e-169"></a>
+<!-- Approximate MVA for single class, closed networks -->
+
+   <p><a name="doc_002dqncsmvaap"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncsmvaap</b> (<var>N, S, V</var>)<var><a name="index-qncsmvaap-170"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncsmvaap</b> (<var>N, S, V, m</var>)<var><a name="index-qncsmvaap-171"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncsmvaap</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qncsmvaap-172"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncsmvaap</b> (<var>N, S, V, m, Z, tol</var>)<var><a name="index-qncsmvaap-173"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncsmvaap</b> (<var>N, S, V, m, Z, tol, iter_max</var>)<var><a name="index-qncsmvaap-174"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029_002c-approximate-175"></a><a name="index-MVA_002c-approximate-176"></a><a name="index-approximate-MVA-177"></a><a name="index-closed-network_002c-single-class-178"></a><a name="index-closed-network_002c-approximate-analysis-179"></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>qncsmvald</code> instead.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Population size (number of requests in the system, <var>N</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)>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) ≥ 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) < 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) > 1</code>).
+
+          <br><dt><var>Z</var><dd>External delay for customers (<var>Z</var><code> ≥ 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>>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) < 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> qncsmva,qncsmvald.
+
+        </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-180"></a><a name="index-Zahorjan_002c-J_002e-181"></a><a name="index-Graham_002c-G_002e-S_002e-182"></a><a name="index-Sevcik_002c-K_002e-C_002e-183"></a>
+<!-- Convolution -->
+
+   <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
+\bf G = \left(G(0), G(1), \ldots, G(N)\right) 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><samp><span class="command">queueing</span></samp> implements the convolution algorithm, in the function
+<samp><span class="command">qncsconv</span></samp> and <samp><span class="command">qncsconvld</span></samp>. The first one
+supports single-station nodes, multiple-station nodes and IS nodes. 
+The second one supports networks with general load-dependent service
+centers.
+
+   <p><a name="doc_002dqncsconv"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qncsconv</b> (<var>N, S, V</var>)<var><a name="index-qncsconv-184"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qncsconv</b> (<var>N, S, V, m</var>)<var><a name="index-qncsconv-185"></a></var><br>
+<blockquote>
+        <p><a name="index-closed-network_002c-single-class-186"></a><a name="index-normalization-constant-187"></a><a name="index-convolution-algorithm-188"></a>
+Analyze product-form, single class closed networks using the convolution algorithm.
+
+        <p>Load-independent service centers, multiple servers (M/M/m
+queues) and IS nodes are supported. For general load-dependent
+service centers, use <code>qncsconvld</code> instead.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>Number of requests in the system (<var>N</var><code>>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) ≥ 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) ≥ 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) < 1</code>, center k is a delay center (IS);
+if <var>m</var><code>(k) ≥ 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> qncsconvld.
+
+        </blockquote></div>
+
+<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–531. <a href="http://doi.acm.org/10.1145/362342.362345">10.1145/362342.362345</a>
+
+   <p><a name="index-Buzen_002c-J_002e-P_002e-189"></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–317.
+
+   <p><a name="index-Bolch_002c-G_002e-190"></a><a name="index-Greiner_002c-S_002e-191"></a><a name="index-de-Meer_002c-H_002e-192"></a><a name="index-Trivedi_002c-K_002e-193"></a>
+<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] = qncsconv( 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><a name="doc_002dqncsconvld"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>, <var>G</var>] = <b>qncsconvld</b> (<var>N, S, V</var>)<var><a name="index-qncsconvld-194"></a></var><br>
+<blockquote>
+        <p><a name="index-closed-network-195"></a><a name="index-normalization-constant-196"></a><a name="index-convolution-algorithm-197"></a><a name="index-load_002ddependent-service-center-198"></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>>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 ≤ n
+≤ 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) ≥ 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> qncsconv.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">Herb Schwetman, <cite>Some Computational Aspects of Queueing Network
+Models</cite>, Technical Report
+<a href="http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-354.pdf">CSD-TR-354</a>, Department of Computer Sciences, Purdue University, feb,
+1981 (revised).
+
+   <p><a name="index-Schwetman_002c-H_002e-199"></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–31,
+1976). SIGMETRICS '76. ACM, New York, NY,
+pp. 109–117. <a href="http://doi.acm.org/10.1145/800200.806187">10.1145/800200.806187</a>
+
+   <p><a name="index-Reiser_002c-M_002e-200"></a><a name="index-Kobayashi_002c-H_002e-201"></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–317. Function <samp><span class="command">qncsconvld</span></samp> 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 <samp><span class="command">qncsconvld</span></samp> which has been made similar to
+function f_i defined in Schwetman, <cite>Some Computational
+Aspects of Queueing Network Models</cite>.
+
+   <p><a name="index-Bolch_002c-G_002e-202"></a><a name="index-Greiner_002c-S_002e-203"></a><a name="index-de-Meer_002c-H_002e-204"></a><a name="index-Trivedi_002c-K_002e-205"></a>
+
+<h4 class="subsection">5.2.3 Non Product-Form QNs</h4>
+
+<p><a name="Non-Product_002dForm-QNs"></a><!-- MVABLO algorithm for approximate analysis of closed, single class -->
+<!-- QN with blocking -->
+<a name="doc_002dqncsmvablo"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncsmvablo</b> (<var>N, S, M, P </var>)<var><a name="index-qncsmvablo-206"></a></var><br>
+<blockquote>
+        <p><a name="index-queueing-network-with-blocking-207"></a><a name="index-blocking-queueing-network-208"></a><a name="index-closed-network_002c-finite-capacity-209"></a><a name="index-MVABLO-210"></a>
+Approximate MVA algorithm for closed 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 < </code><var>N</var><code> < sum(</code><var>M</var><code>)</code>.
+
+          <br><dt><var>S</var><dd>Average service time. <var>S</var><code>(k)</code> is the average service time
+requested on server k (<var>S</var><code>(k) > 0</code>).
+
+          <br><dt><var>M</var><dd><var>M</var><code>(k)</code> is the capacity of center
+k. The capacity is the maximum number of requests in a service
+center, including the request currently in service (<var>M</var><code>(k) ≥ 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>(k)</code> is the utilization of
+service center i.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(k)</code> is the average response time
+of service center i.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(k)</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 class="noindent">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–428.  <a href="http://dx.doi.org/10.1109/32.4663">10.1109/32.4663</a>
+
+   <p><a name="index-Akyildiz_002c-I_002e-F_002e-211"></a>
+<a name="doc_002dqnmarkov"></a>
+
+<div class="defun">
+— 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-212"></a></var><br>
+— 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-213"></a></var><br>
+— 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-214"></a></var><br>
+— 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-215"></a></var><br>
+<blockquote>
+        <p><a name="index-closed-network_002c-multiple-classes-216"></a><a name="index-closed-network_002c-finite-capacity-217"></a><a name="index-blocking-queueing-network-218"></a><a name="index-RS-blocking-219"></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
+k has m_k > 0 servers, and has a total (finite) capacity of
+C_k \geq m_k which includes both buffer space and servers. 
+The buffer space at service center k is therefore
+C_k - m_k.
+
+          <li>The network can be open, with external arrival rate to
+center k equal to
+\lambda_k, or closed with fixed
+population size N. For closed networks, the population size
+N must be strictly less than the network capacity: N < \sum_i C_i.
+
+          <li>Average service times are load-independent.
+
+          <li>P_i, j 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_i, j.
+
+          <li>Blocking type is Repetitive-Service (RS). Service
+center j is <em>saturated</em> if the number of requests is equal
+to its capacity C_j. 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>(k) ≥ 0</code> to service center
+k 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> < sum(</code><var>C</var><code>)</code>.
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the average service time at service center
+k
+
+          <br><dt><var>C</var><dd><var>C</var><code>(k)</code> is the Capacity of service center k. The capacity includes both
+the buffer and server space <var>m</var><code>(i)</code>. Thus the buffer space is
+<var>C</var><code>(k)-</code><var>m</var><code>(k)</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>(k)</code> is the number of servers at service center
+k. Note that <var>m</var><code>(k) ≥ </code><var>C</var><code>(k)</code> for each <var>k</var>. 
+If <var>m</var> is omitted, all service centers are assumed to have a
+single server (<var>m</var><code>(k) = 1</code> for all k).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>U</var><dd><var>U</var><code>(k)</code> is the utilization of 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 customers in the
+service center k, <em>including</em> the request in service.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(k)</code> is the throughput of service center k.
+
+        </dl>
+
+        <blockquote>
+<b>Note:</b> 
+The space complexity of this implementation is
+O( \prod_k=1^K (C_k + 1)^2). The time complexity is dominated
+by the time needed to solve a linear system with
+\prod_k=1^K (C_k + 1)
+unknowns.
+
+        </blockquote>
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Multiple-Class-Models"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Generic-Algorithms">Generic Algorithms</a>,
+Previous: <a rel="previous" accesskey="p" href="#Single-Class-Models">Single Class Models</a>,
+Up: <a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">5.3 Multiple Class Models</h3>
+
+<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_c, k in service at service center
+k. For open models, we denote with \bf \lambda =
+\lambda_c, k the arrival rates, where \lambda_c, k 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_r, i, s, j]
+such that P_r, i, s, j 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_{c, k}<dd>(open networks) External arrival rate of class-c requests to service center k
+
+     <br><dt>lambda<dd>(open networks) Overall external arrival rate to the whole system: \lambda = \sum_c \sum_k \lambda_c, k
+
+     <br><dt>N_c<dd>(closed networks) Number of class c requests in the system.
+
+     <br><dt>S_c, k<dd>Average service time. S_c, k is the average service time on
+service center k for class c requests.
+
+     <br><dt>P_r, i, s, j<dd>Routing probability matrix. \bf P = [P_r, i, s, j] is a C
+\times K \times C \times K matrix such that P_r, i, s, j 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_c, k<dd>Mean number of visits of class c requests to center k.
+
+   </dl>
+
+<p class="noindent"><strong>Model Outputs</strong>
+
+     <dl>
+<dt>U_c, k<dd>Utilization of service center k 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).  If center k
+is a single-server or multiserver node, then
+0 \leq U_c, k \leq 1. 
+If center k is an infinite server node (delay
+center), then U_c, k denotes the <em>traffic intensity</em> and
+is defined as U_c, k = X_c, k S_c, k; in this case the
+utilization may be greater than one.
+
+     <br><dt>R_c, k<dd>Average response time experienced by class c requests on service
+center k. 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_c, k<dd>Average number of class c requests on service center
+k. This includes both the requests in the queue, and the request
+being served.
+
+     <br><dt>X_c, k<dd>Throughput of service center k 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_k<dd>Utilization of service center k:
+<code>Uk = sum(U,k);</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>X(c) = X(c,k) ./ V(c,k);</code> for any k for which <code>V(c,k) != 0</code>
+
+   </dl>
+
+   <p>For closed networks, we can define the visit ratios V_s, j
+for class s customers at service center j as follows:
+
+   <p>V_sj = sum_r sum_i V_ri P_risj    s=1,...,C, j=1,...,K
+V_s r_s = 1                       s=1,...,C
+
+<p class="noindent">where r_s is the class s
+reference station. Similarly to single class models, the traffic
+equation for closed multiclass networks can be solved up to
+multiplicative constants unless we choose one reference station for
+each closed chain class and set its visit ratio to 1.
+
+   <p>For open networks the traffic equations are as follows:
+
+   <p>V_sj = P_0sj + sum_r sum_i V_ri P_risj  s=1,...,C, j=1,...,K
+
+<p class="noindent">where P_0, s, j is the probability that an external
+arrival goes to service center j as a class-s request. 
+If \lambda_s, j is the external arrival rate of class
+s requests to service center j, and \lambda =
+\sum_s \sum_j \lambda_s, j is the overall external arrival rate,
+then P_0, s, j = \lambda_s, j / \lambda.
+
+   <p><a name="doc_002dqncmvisits"></a>
+
+<div class="defun">
+— Function File: [<var>V</var> <var>ch</var>] = <b>qncmvisits</b> (<var>P</var>)<var><a name="index-qncmvisits-220"></a></var><br>
+— Function File: [<var>V</var> <var>ch</var>] = <b>qncmvisits</b> (<var>P, r</var>)<var><a name="index-qncmvisits-221"></a></var><br>
+<blockquote>
+        <p>Compute the average number of visits to the service centers of a closed multiclass network with K service centers and C customer classes.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd><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 allowed.
+
+          <br><dt><var>r</var><dd><var>r</var><code>(c)</code> is the index of class c reference station,
+r(c) \in {1, <small class="dots">...</small>, K}, c \in {1, <small class="dots">...</small>, C}. 
+The class c visit count to server <var>r</var><code>(c)</code>
+(<var>V</var><code>(c,r(c))</code>) is conventionally set to 1. The reference
+station serves two purposes: (i) its throughput is assumed to be the
+system throughput, and (ii) a job returning to the reference station
+is assumed to have completed one cycle. Default is to consider
+station 1 as the reference station for all classes.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>V</var><dd><var>V</var><code>(c,i)</code> is the number of visits of class c
+requests at center i.
+
+          <br><dt><var>ch</var><dd><var>ch</var><code>(c)</code> is the chain number that class c belongs
+to. Different classes can belong to the same chain. Chains are
+numbered sequentially starting from 1 (1, 2, <small class="dots">...</small>). The
+total number of chains is <code>max(</code><var>ch</var><code>)</code>.
+
+        </dl>
+
+        </blockquote></div>
+
+   <p><a name="doc_002dqnomvisits"></a>
+
+<div class="defun">
+— Function File: <var>V</var> = <b>qnomvisits</b> (<var>P, lambda</var>)<var><a name="index-qnomvisits-222"></a></var><br>
+<blockquote>
+        <p>Compute the visit ratios to the service centers of an open multiclass network with K service centers and C customer classes.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>P</var><dd><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><var>lambda</var><code>(r,i)</code> is the external arrival rate of class r
+requests to center i.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>V</var><dd><var>V</var><code>(r,i)</code> is the visit ratio of class r
+requests at center i.
+
+        </dl>
+
+        </blockquote></div>
+
+<!-- Open Networks -->
+<h4 class="subsection">5.3.1 Open Networks</h4>
+
+<!-- Open network with multiple classes -->
+<p><a name="doc_002dqnom"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnom</b> (<var>lambda, S, V</var>)<var><a name="index-qnom-223"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnom</b> (<var>lambda, S, V, m</var>)<var><a name="index-qnom-224"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnom</b> (<var>lambda, S, P</var>)<var><a name="index-qnom-225"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qnom</b> (<var>lambda, S, P, m</var>)<var><a name="index-qnom-226"></a></var><br>
+<blockquote>
+        <p><a name="index-open-network_002c-multiple-classes-227"></a><a name="index-multiclass-network_002c-open-228"></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.
+
+        <blockquote>
+<b>Note:</b> If this function is called specifying the visit ratios
+<var>V</var>, class switching is <strong>not</strong> allowed. 
+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 servers or delay
+centers: multiple-server and general load-dependent centers are not
+supported. 
+Note that the meaning of parameter <var>lambda</var> is different
+from one case to the other (see below). 
+</blockquote>
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>If this function is invoked as <code>qnom(lambda, S, V, ...)</code>,
+then <var>lambda</var><code>(c)</code> is the external arrival rate of class
+c customers (<var>lambda</var><code>(c) ≥ 0</code>). If this
+function is invoked as <code>qnom(lambda, S, P, ...)</code>, then
+<var>lambda</var><code>(c,k)</code> is the external arrival rate of class
+c customers at center k (<var>lambda</var><code>(c,k) ≥
+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)>0</code>). 
+For FCFS nodes, mean service times must be class-independent.
+
+          <br><dt><var>V</var><dd><var>V</var><code>(c,k)</code> is the visit ratio of class c
+customers to service center k (<var>V</var><code>(c,k) ≥ 0 </code>). 
+<strong>If you pass this argument, 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 argument </strong><var>P</var><strong>,
+class switching is allowed</strong>; however, all servers must be fixed-rate or infinite-server nodes (<var>m</var><code>(k) ≤ 1</code> for all k).
+
+          <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) < 1</code>, enter 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 all k.
+
+        </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,qnos,qnomvisits.
+
+        </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 7.4.1 ("Open Model Solution Techniques").
+
+   <p><a name="index-Lazowska_002c-E_002e-D_002e-229"></a><a name="index-Zahorjan_002c-J_002e-230"></a><a name="index-Graham_002c-G_002e-S_002e-231"></a><a name="index-Sevcik_002c-K_002e-C_002e-232"></a>
+
+<h4 class="subsection">5.3.2 Closed Networks</h4>
+
+<p><a name="doc_002dqncmpopmix"></a>
+
+<div class="defun">
+— Function File: pop_mix = <b>qncmpopmix</b> (<var>k, N</var>)<var><a name="index-qncmpopmix-233"></a></var><br>
+<blockquote>
+        <p><a name="index-population-mix-234"></a><a name="index-closed-network_002c-multiple-classes-235"></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>(c)</code> requests of class c, a
+k-mix <var>mix</var> is a C-dimensional vector with the
+following properties:
+
+     <pre class="example">          all( mix >= 0 );
+          all( mix <= 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>(c)</code> is the number of class c requests. 
+The condition <var>k</var><code> ≤ 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,c)</code> is the number of class c 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> qncmnpop.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">Herb Schwetman, <cite>Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models</cite>, Technical Report
+<a href="http://www.cs.purdue.edu/research/technical_reports/1980/TR 80-355.pdf">CSD-TR-355</a>,
+Department of Computer Sciences, Purdue University, feb 15, 1982,
+
+   <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
+≥ 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-236"></a><a name="index-Santini_002c-S_002e-237"></a>
+<a name="doc_002dqncmnpop"></a>
+
+<div class="defun">
+— Function File: <var>H</var> = <b>qncmnpop</b> (<var>N</var>)<var><a name="index-qncmnpop-238"></a></var><br>
+<blockquote>
+        <p><a name="index-population-mix-239"></a><a name="index-closed-network_002c-multiple-classes-240"></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> qncmmva,qncmpopmix.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">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">10.1145/1010629.805477</a>
+
+   <p><a name="index-Zahorjan_002c-J_002e-241"></a><a name="index-Wong_002c-E_002e-242"></a>
+<!-- MVA for multiple class, closed networks -->
+<a name="doc_002dqncmmva"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmva</b> (<var>N, S </var>)<var><a name="index-qncmmva-243"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmva</b> (<var>N, S, V</var>)<var><a name="index-qncmmva-244"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmva</b> (<var>N, S, V, m</var>)<var><a name="index-qncmmva-245"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmva</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qncmmva-246"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmva</b> (<var>N, S, P</var>)<var><a name="index-qncmmva-247"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmva</b> (<var>N, S, P, r</var>)<var><a name="index-qncmmva-248"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmva</b> (<var>N, S, P, r, m</var>)<var><a name="index-qncmmva-249"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029-250"></a><a name="index-closed-network_002c-multiple-classes-251"></a><a name="index-multiclass-network_002c-closed-252"></a>
+Compute steady-state performance measures for closed, multiclass
+queueing networks 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 servers 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) ≥ 0</code>. If class c has
+no requests (<var>N</var><code>(c) == 0</code>), then for all <var>k</var>,
+<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>
+
+          <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) ≥ 0</code>). If the
+service time at center k is class-dependent, i.e., different
+classes have different service times at center k, then center
+k is assumed to be of type -/G/1–PS (Processor
+Sharing). 
+If center k is a FCFS node (<var>m</var><code>(k)>1</code>), then the
+service times <strong>must</strong> be class-independent, i.e., all classes
+<strong>must</strong> have the same service time.
+
+          <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) ≥ 0</code>,
+default is 1. 
+<strong>If you pass this argument, 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; the reference stations for each class
+are specified with the paramter <var>r</var>. 
+<strong>If you pass argument </strong><var>P</var><strong>,
+class switching is allowed</strong>; however, you can not specify any external delay
+(i.e., <var>Z</var> must be zero) and all servers must be fixed-rate or infinite-server nodes (<var>m</var><code>(k) ≤ 1</code> for all k).
+
+          <br><dt><var>r</var><dd><var>r</var><code>(c)</code> is the reference station for class c. 
+If omitted, station 1 is the reference station for all classes. 
+See <samp><span class="command">qncmvisits</span></samp>.
+
+          <br><dt><var>m</var><dd>If <var>m</var><code>(k)<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–FCFS, -/G/1–LCFS-PR or -/G/1–PS). 
+Finally, if <var>m</var><code>(k)>1</code>, center k is a
+M/M/m–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) ≥ 0</code>. Default is 0. This parameter can not be
+used if you pass a routing matrix as the second parameter of
+<code>qncmmva</code>.
+
+        </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>(c,k)</code> is the class c utilization at
+center k, 0 ≤ U(c,k) ≤ 1. 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>. In this case the value of
+<var>U</var><code>(c,k)</code> may be greater than one.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(c,k)</code> is the class c response time at
+center k. The class c <em>residence time</em>
+at center k is <var>R</var><code>(c,k) * </code><var>C</var><code>(c,k)</code>. 
+The total class c system response time
+is <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 throughput can be computed
+as <var>X</var><code>(c,1) / </code><var>V</var><code>(c,1)</code>.
+
+        </dl>
+
+        <blockquote>
+<b>Note on numerical stability:</b> In presence of load-dependent servers (e.g., if <var>m</var><code>(i)>1</code>
+for some i), the MVA algorithm is known to be numerically
+unstable. Generally this problem manifests itself as negative
+response times or utilizations. This is not a problem with the
+<code>queueing</code> toolbox, but with the Mean Value Analysis algorithm,
+and therefore has currently no easy workaround (aoart from using a
+different solution technique, if available). This function prints a
+warning if it detects numerical problems; you can disable the warning
+with the command <code>warning("off", "qn:numerical-instability")</code>. 
+</blockquote>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosed, qncmmvaapprox, qncmvisits.
+
+        </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">qncmmvaap</span></samp> function, which implements
+the approximate MVA algorithm. Note however that <samp><span class="command">qncmmvaap</span></samp>
+will only provide approximate results.
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">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–322. <a href="http://doi.acm.org/10.1145/322186.322195">10.1145/322186.322195</a>
+
+   <p><a name="index-Reiser_002c-M_002e-253"></a><a name="index-Lavenberg_002c-S_002e-S_002e-254"></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-255"></a><a name="index-Greiner_002c-S_002e-256"></a><a name="index-de-Meer_002c-H_002e-257"></a><a name="index-Trivedi_002c-K_002e-258"></a><a name="index-Lazowska_002c-E_002e-D_002e-259"></a><a name="index-Zahorjan_002c-J_002e-260"></a><a name="index-Graham_002c-G_002e-S_002e-261"></a><a name="index-Sevcik_002c-K_002e-C_002e-262"></a>
+<!-- Approximate MVA, with Bard-Schweitzer approximation -->
+<a name="doc_002dqncmmvaap"></a>
+
+<div class="defun">
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmvaap</b> (<var>N, S, V</var>)<var><a name="index-qncmmvaap-263"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmvaap</b> (<var>N, S, V, m</var>)<var><a name="index-qncmmvaap-264"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmvaap</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qncmmvaap-265"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmvaap</b> (<var>N, S, V, m, Z, tol</var>)<var><a name="index-qncmmvaap-266"></a></var><br>
+— Function File: [<var>U</var>, <var>R</var>, <var>Q</var>, <var>X</var>] = <b>qncmmvaap</b> (<var>N, S, V, m, Z, tol, iter_max</var>)<var><a name="index-qncmmvaap-267"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029_002c-approximate-268"></a><a name="index-MVA_002c-approximate-269"></a><a name="index-closed-network_002c-multiple-classes-270"></a><a name="index-multiclass-network_002c-closed-271"></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)>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) ≥ 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) ≥ 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) < 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>>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>>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> qncmmva.
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">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–62.
+
+   <p><a name="index-Bard_002c-Y_002e-272"></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–29.
+
+   <p><a name="index-Schweitzer_002c-P_002e-273"></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-274"></a><a name="index-Zahorjan_002c-J_002e-275"></a><a name="index-Graham_002c-G_002e-S_002e-276"></a><a name="index-Sevcik_002c-K_002e-C_002e-277"></a>
+
+<h4 class="subsection">5.3.3 Mixed Networks</h4>
+
+<!-- MVA for mixed networks -->
+<p><a name="doc_002dqnmix"></a>
+
+<div class="defun">
+— 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-278"></a></var><br>
+<blockquote>
+        <p><a name="index-Mean-Value-Analysys-_0028MVA_0029-279"></a><a name="index-mixed-network-280"></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>qnom</code> or <code>qncmmva</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)>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)>0</code> is the arrival rate of class c
+requests and <var>N</var><code>(c)</code> must be zero;
+
+          </ul>
+
+          <p class="noindent">In other words, for each class c the following must hold:
+
+          <pre class="example">               (<var>lambda</var>(c)>0 && <var>N</var>(c)==0) || (<var>lambda</var>(c)==0 && <var>N</var>(c)>0)
+</pre>
+          <br><dt><var>S</var><dd><var>S</var><code>(c,k)</code> is the mean class c service time at center
+k, <var>S</var><code>(c,k) ≥ 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 center k (<var>V</var><code>(c,k) ≥ 0</code>).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center k. Only
+single-server (<var>m</var><code>(k)==1</code>) or IS (Infinite Server) nodes
+(<var>m</var><code>(k)<1</code>) are supported. If omitted, each center
+is assumed to be of type M/M/1-FCFS. 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 class c utilization at center k.
+
+          <br><dt><var>R</var><dd><var>R</var><code>(c,k)</code> is class c response time at center k.
+
+          <br><dt><var>Q</var><dd><var>Q</var><code>(c,k)</code> is the average number of
+class c requests at center k.
+
+          <br><dt><var>X</var><dd><var>X</var><code>(c,k)</code> is class c throughput at center k.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qncmmva, qncm.
+
+        </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 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-281"></a><a name="index-Zahorjan_002c-J_002e-282"></a><a name="index-Graham_002c-G_002e-S_002e-283"></a><a name="index-Sevcik_002c-K_002e-C_002e-284"></a>
+Herb Schwetman, <cite>Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models</cite>, Technical Report
+<a href="http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-355.pdf">CSD-TR-355</a>, Department of Computer Sciences, Purdue University, feb
+15, 1982,
+
+   <p><a name="index-Schwetman_002c-H_002e-285"></a>
+<div class="node">
+<a name="Generic-Algorithms"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Bounds-Analysis">Bounds Analysis</a>,
+Previous: <a rel="previous" accesskey="p" href="#Multiple-Class-Models">Multiple Class Models</a>,
+Up: <a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">5.4 Generic Algorithms</h3>
+
+<p>The <code>queueing</code> package provides a high-level function
+<samp><span class="command">qnsolve</span></samp> for analyzing QN models. <samp><span class="command">qnsolve</span></samp> takes as
+input a high-level description of the queueing model, and delegates
+the actual solution of the model to one of the lower-level
+function. <samp><span class="command">qnsolve</span></samp> supports single or multiclass models, but at
+the moment only product-form networks can be analyzed. For non
+product-form networks See <a href="#Non-Product_002dForm-QNs">Non Product-Form QNs</a>.
+
+   <p><samp><span class="command">qnsolve</span></samp> accepts two input parameters. The first one is the list
+of nodes, encoded as an Octave <em>cell array</em>. The second parameter
+is the vector of visit ratios <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
+<samp><span class="command">qnmknode</span></samp> function.
+
+   <p><a name="doc_002dqnmknode"></a>
+
+<div class="defun">
+— Function File: <var>Q</var> = <b>qnmknode</b> (<var>"m/m/m-fcfs", S</var>)<var><a name="index-qnmknode-286"></a></var><br>
+— Function File: <var>Q</var> = <b>qnmknode</b> (<var>"m/m/m-fcfs", S, m</var>)<var><a name="index-qnmknode-287"></a></var><br>
+— Function File: <var>Q</var> = <b>qnmknode</b> (<var>"m/m/1-lcfs-pr", S</var>)<var><a name="index-qnmknode-288"></a></var><br>
+— Function File: <var>Q</var> = <b>qnmknode</b> (<var>"-/g/1-ps", S</var>)<var><a name="index-qnmknode-289"></a></var><br>
+— Function File: <var>Q</var> = <b>qnmknode</b> (<var>"-/g/1-ps", S, s2</var>)<var><a name="index-qnmknode-290"></a></var><br>
+— Function File: <var>Q</var> = <b>qnmknode</b> (<var>"-/g/inf", S</var>)<var><a name="index-qnmknode-291"></a></var><br>
+— Function File: <var>Q</var> = <b>qnmknode</b> (<var>"-/g/inf", S, s2</var>)<var><a name="index-qnmknode-292"></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>Mean service time.
+
+               <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
+<samp><span class="command">qnsolve</span></samp>.
+
+   <p><a name="doc_002dqnsolve"></a>
+
+<div class="defun">
+— 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-293"></a></var><br>
+— 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-294"></a></var><br>
+— 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-295"></a></var><br>
+— 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-296"></a></var><br>
+<blockquote>
+        <p>High-level function for analyzing QN models.
+
+          <ul>
+<li>For <strong>closed</strong> networks, the following server types are
+supported: M/M/m–FCFS, -/G/\infty, -/G/1–LCFS-PR,
+-/G/1–PS and load-dependent variants.
+
+          <li>For <strong>open</strong> networks, the following server types are supported:
+M/M/m–FCFS, -/G/\infty and -/G/1–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–FCFS.
+
+          <li>For <strong>mixed</strong> networks, the following server types are supported:
+M/M/1–FCFS, -/G/\infty and -/G/1–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–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–PS node, with service times
+S_1, 2 = 0.4 for class 1, and S_2, 2 = 0.6 for class 2
+requests;
+
+     <li>Node 3 is a -/G/\infty node (delay center), with service
+times S_1, 3=1 and S_2, 3=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>
+   <p><a name="doc_002dqnclosed"></a>
+
+<div class="defun">
+— 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-297"></a></var><br>
+<blockquote>
+        <p><a name="index-closed-network_002c-single-class-298"></a><a name="index-closed-network_002c-multiple-classes-299"></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>qncsemva</code>, <code>qncsmvald</code> or <code>qncmmva</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>qncsmva</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 center k when i=1, <small class="dots">...</small>, N
+jobs are present; in this case, the network is analyzed with the
+<code>qncmmvald</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>qncmmva</code> function.
+
+        </ul>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qncsmva, qncsmvald, qncmmva.
+
+        </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(size(S));             # All centers are single-server
+      Z = 2;                         # External delay
+      N = 15;                        # Maximum population to consider
+      V = qncsvisits(P);             # Compute number of visits
+      X_bsb_lower = X_bsb_upper = X_ab_lower = X_ab_upper = X_mva = zeros(1,N);
+      for n=1:N
+        [X_bsb_lower(n) X_bsb_upper(n)] = qncsbsb(n, S, V, m, Z);
+        [X_ab_lower(n) X_ab_upper(n)] = qncsaba(n, S, V, m, 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]); legend("location","southeast");
+      xlabel("Number of Requests n"); ylabel("System Throughput X(n)");
+</pre>
+</pre>
+   <p><a name="doc_002dqnopen"></a>
+
+<div class="defun">
+— 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-300"></a></var><br>
+<blockquote>
+        <p><a name="index-open-network-301"></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> qnos, qnom.
+
+        </blockquote></div>
+
+<div class="node">
+<a name="Bounds-Analysis"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#QN-Analysis-Examples">QN Analysis Examples</a>,
+Previous: <a rel="previous" accesskey="p" href="#Generic-Algorithms">Generic Algorithms</a>,
+Up: <a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">5.5 Bounds Analysis</h3>
+
+<p><a name="doc_002dqnosaba"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnosaba</b> (<var>lambda, D</var>)<var><a name="index-qnosaba-302"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnosaba</b> (<var>lambda, S, V</var>)<var><a name="index-qnosaba-303"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnosaba</b> (<var>lambda, S, V, m</var>)<var><a name="index-qnosaba-304"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-asymptotic-305"></a><a name="index-open-network-306"></a>
+Compute Asymptotic Bounds for open, single-class networks.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>Arrival rate of requests (<var>lambda</var><code> ≥ 0</code>).
+
+          <br><dt><var>D</var><dd><var>D</var><code>(k)</code> is the service demand at center k. 
+(<var>D</var><code>(k) ≥ 0</code> for all k).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the mean service time at center k. 
+(<var>S</var><code>(k) ≥ 0</code> for all k).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the mean number of visits to center k. 
+(<var>V</var><code>(k) ≥ 0</code> for all k).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center k. 
+This function only supports M/M/1 queues, therefore
+<var>m</var> must be <code>ones(size(S))</code>.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><br><dt><var>Xu</var><dd>Lower and upper bounds on the system throughput. <var>Xl</var> is
+always set to 0 since there can be no lower bound on the
+throughput of open networks.
+
+          <br><dt><var>Rl</var><br><dt><var>Ru</var><dd>Lower and upper bounds on the system response time. <var>Ru</var>
+is always set to <code>+inf</code> since there can be no upper bound on the
+throughput of open networks.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnopenmultiab.
+
+        </blockquote></div>
+
+   <p><a name="doc_002dqnomaba"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnomaba</b> (<var>lambda, D</var>)<var><a name="index-qnomaba-307"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Rl</var>] = <b>qnomaba</b> (<var>lambda, S, V</var>)<var><a name="index-qnomaba-308"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-asymptotic-309"></a><a name="index-open-network-310"></a><a name="index-multiclass-network_002c-open-311"></a>
+Compute Asymptotic Bounds for open, multiclass networks.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd><var>lambda</var><code>(c)</code> is the class c arrival rate to the
+system.
+
+          <br><dt><var>D</var><dd><var>D</var><code>(c, k)</code> is class c service demand
+at center k. (<var>D</var><code>(c, k) ≥ 0</code> for all
+k).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(c, k)</code> is the mean service time of class c
+requests at center k. (<var>S</var><code>(c, k) ≥ 0</code> for all
+k).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(c, k)</code> is the mean number of visits of class c
+requests at center k. (<var>V</var><code>(c, k) ≥ 0</code> for all
+k).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><br><dt><var>Xu</var><dd>Per-class lower and upper throughput bounds. For example,
+<var>Xu</var><code>(c)</code> is the upper bound for class c throughput. 
+<code>Xl</code> is always 0 since there can be no lower bound
+on the throughput of open networks.
+
+          <br><dt><var>Rl</var><br><dt><var>Ru</var><dd>Per-class lower and upper response time bounds. 
+<code>Ru</code> is always <code>+inf</code> since there can be no upper bound
+on the response time of open networks.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnombsb.
+
+        </blockquote></div>
+
+   <p><a name="doc_002dqncsaba"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncsaba</b> (<var>N, D</var>)<var><a name="index-qncsaba-312"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncsaba</b> (<var>N, S, V</var>)<var><a name="index-qncsaba-313"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncsaba</b> (<var>N, S, V, m</var>)<var><a name="index-qncsaba-314"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncsaba</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qncsaba-315"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-asymptotic-316"></a><a name="index-asymptotic-bounds-317"></a><a name="index-closed-network_002c-single-class-318"></a>
+Compute Asymptotic Bounds for throughput and response time of closed, single-class networks.
+
+        <p>Single-server and infinite-server nodes are supported. 
+Multiple-server nodes and general load-dependent servers are not
+supported.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>number of requests in the system (scalar, <var>N</var><code>>0</code>).
+
+          <br><dt><var>D</var><dd><var>D</var><code>(k)</code> is the service demand at center k
+(<var>D</var><code>(k) ≥ 0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the mean service time at center k
+(<var>S</var><code>(k) ≥ 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) ≥ 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) < 1</code>, center k is a delay center (IS);
+if <var>m</var><code>(k) = 1</code>, center k is a M/M/1-FCFS server. 
+This function does not support multiple-server nodes. Default
+is 1.
+
+          <br><dt><var>Z</var><dd>External delay (<var>Z</var><code> ≥ 0</code>). Default is 0.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dt><var>Xu</var><dd>Lower and upper system throughput bounds.
+
+          <br><dt><var>Rl</var><dt><var>Ru</var><dd>Lower and upper response time bounds.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qncmaba.
+
+        </blockquote></div>
+
+   <p><a name="doc_002dqncmaba"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncmaba</b> (<var>N, D</var>)<var><a name="index-qncmaba-319"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncmaba</b> (<var>N, S, V</var>)<var><a name="index-qncmaba-320"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncmaba</b> (<var>N, S, V, m</var>)<var><a name="index-qncmaba-321"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncmaba</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qncmaba-322"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-asymptotic-323"></a><a name="index-asymptotic-bounds-324"></a><a name="index-closed-network-325"></a><a name="index-multiclass-network_002c-closed-326"></a><a name="index-closed-multiclass-network-327"></a>
+Compute Asymptotic Bounds for multiclass networks. 
+Single-server and infinite-server nodes are supported. 
+Multiple-server nodes and general load-dependent servers are not
+supported.
+
+        <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.
+
+          <br><dt><var>D</var><dd><var>D</var><code>(c, k)</code> is class c service demand
+at center k (<var>D</var><code>(c,k) ≥ 0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(c, k)</code> is the mean service time of class c
+requests at center k (<var>S</var><code>(c,k) ≥ 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) ≥ 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) < 1</code>, center k is a delay center (IS);
+if <var>m</var><code>(k) = 1</code>, center k is a M/M/1-FCFS server. 
+This function does not support multiple-server nodes. Default
+is 1.
+
+          <br><dt><var>Z</var><dd><var>Z</var><code>(c)</code> is class c external delay
+(<var>Z</var><code>(c) ≥ 0</code>). Default is 0.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dt><var>Xu</var><dd>Lower and upper class c throughput bounds.
+
+          <br><dt><var>Rl</var><dt><var>Ru</var><dd>Lower and upper class c response time bounds.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedsingleab.
+
+        </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-328"></a><a name="index-Zahorjan_002c-J_002e-329"></a><a name="index-Graham_002c-G_002e-S_002e-330"></a><a name="index-Sevcik_002c-K_002e-C_002e-331"></a>
+<a name="doc_002dqnosbsb"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnosbsb</b> (<var>lambda, D</var>)<var><a name="index-qnosbsb-332"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qnosbsb</b> (<var>lambda, S, V</var>)<var><a name="index-qnosbsb-333"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-balanced-system-334"></a><a name="index-open-network-335"></a>
+Compute Balanced System Bounds for single-class, open networks.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>lambda</var><dd>overall arrival rate to the system (scalar). Abort if
+<var>lambda</var><code> < 0 </code>
+
+          <br><dt><var>D</var><dd><var>D</var><code>(k)</code> is the service demand at center k. 
+(<var>D</var><code>(k) ≥ 0</code> for all k).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the mean service time at center k. 
+(<var>S</var><code>(k) ≥ 0</code> for all k).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the mean number of visits at center k. 
+(<var>V</var><code>(k) ≥ 0</code> for all k).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center k. 
+This function only supports M/M/1 queues, therefore
+<var>m</var> must be <code>ones(size(S))</code>.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><br><dt><var>Xu</var><dd>Lower and upper bounds on the system throughput. <var>Xl</var> is always
+set to 0, since there can be no lower bound on open
+networks throughput.
+
+          <br><dt><var>Rl</var><dt><var>Ru</var><dd>Lower and upper bounds on the system response time.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnosaba.
+
+        </blockquote></div>
+
+   <p><a name="doc_002dqncsbsb"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncsbsb</b> (<var>N, D</var>)<var><a name="index-qncsbsb-336"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncsbsb</b> (<var>N, S, V</var>)<var><a name="index-qncsbsb-337"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncsbsb</b> (<var>N, S, V, m</var>)<var><a name="index-qncsbsb-338"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncsbsb</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qncsbsb-339"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-balanced-system-340"></a><a name="index-closed-network_002c-single-class-341"></a><a name="index-balanced-system-bounds-342"></a>
+Compute Balanced System Bounds on system throughput and response time for closed, single-class networks.
+
+        <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>D</var><code>(k) ≥ 0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the mean service time at center k
+(<var>S</var><code>(k) ≥ 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) ≥ 0</code>). Default is 1.
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center k. This
+function supports <var>m</var><code>(k) = 1</code> only (sing-eserver FCFS
+nodes). This option is left for compatibility with
+<code>qncsaba</code>, Default is 1.
+
+          <br><dt><var>Z</var><dd>External delay (<var>Z</var><code> ≥ 0</code>). Default is 0.
+
+        </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> qncmbsb.
+
+        </blockquote></div>
+
+   <p><a name="doc_002dqncmbsb"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncmbsb</b> (<var>N, D</var>)<var><a name="index-qncmbsb-343"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncmbsb</b> (<var>N, S, V</var>)<var><a name="index-qncmbsb-344"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-balanced-system-345"></a><a name="index-balanced-system-bounds-346"></a><a name="index-multiclass-network_002c-closed-347"></a><a name="index-closed-multiclass-network-348"></a>
+Compute Balanced System Bounds for multiclass networks. 
+Only single-server nodes are supported.
+
+        <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.
+
+          <br><dt><var>D</var><dd><var>D</var><code>(c, k)</code> is class c service demand
+at center k (<var>D</var><code>(c,k) ≥ 0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(c, k)</code> is the mean service time of class c
+requests at center k (<var>S</var><code>(c,k) ≥ 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) ≥ 0</code>).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dt><var>Xu</var><dd>Lower and upper class c throughput bounds.
+
+          <br><dt><var>Rl</var><dt><var>Ru</var><dd>Lower and upper class c response time bounds.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qnclosedsinglebsb.
+
+        </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.4 ("Balanced Systems Bounds").
+
+   <p><a name="index-Lazowska_002c-E_002e-D_002e-349"></a><a name="index-Zahorjan_002c-J_002e-350"></a><a name="index-Graham_002c-G_002e-S_002e-351"></a><a name="index-Sevcik_002c-K_002e-C_002e-352"></a>
+<a name="doc_002dqncmcb"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncmcb</b> (<var>N, D</var>)<var><a name="index-qncmcb-353"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncmcb</b> (<var>N, S, V</var>)<var><a name="index-qncmcb-354"></a></var><br>
+<blockquote>
+        <p><a name="index-multiclass-network_002c-closed-355"></a><a name="index-closed-multiclass-network-356"></a><a name="index-bounds_002c-composite-357"></a><a name="index-composite-bounds-358"></a>
+Composite Bound (CB) on throughput and response time for closed multiclass networks.
+
+        <p>This function implements the Composite Bound Method described in T. 
+Kerola, <cite>The Composite Bound Method (CBM) for Computing
+Throughput Bounds in Multiple Class Environments</cite>, Technical Report
+CSD-TR-475, Purdue University, march 13, 1984 (revised august 27,
+1984).
+
+        <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.
+
+          <br><dt><var>D</var><dd><var>D</var><code>(c, k)</code> is class c service demand
+at center k (<var>S</var><code>(c,k) ≥ 0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(c, k)</code> is the mean service time of class c
+requests at center k (<var>S</var><code>(c,k) ≥ 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) ≥ 0</code>).
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dt><var>Xu</var><dd>Lower and upper class c throughput bounds.
+
+          <br><dt><var>Rl</var><dt><var>Ru</var><dd>Lower and upper class c response time bounds.
+
+        </dl>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">Teemu Kerola, <cite>The Composite Bound Method (CBM) for Computing
+Throughput Bounds in Multiple Class Environments</cite>, Technical Report
+CSD-TR-475, Department of Computer Sciences, Purdue University, mar 13,
+1984 (Revised aug 27, 1984).
+
+   <p><a name="index-Kerola_002c-T_002e-359"></a>
+<a name="doc_002dqncspb"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncspb</b> (<var>N, D </var>)<var><a name="index-qncspb-360"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncspb</b> (<var>N, S, V </var>)<var><a name="index-qncspb-361"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncspb</b> (<var>N, S, V, m </var>)<var><a name="index-qncspb-362"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>] = <b>qncspb</b> (<var>N, S, V, m, Z </var>)<var><a name="index-qncspb-363"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-PB-364"></a><a name="index-PB-bounds-365"></a><a name="index-closed-network_002c-single-class-366"></a>
+Compute PB Bounds (C. H. Hsieh and S. Lam, 1987) for single-class,
+closed networks.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>number of requests in the system (scalar). Must be <var>N</var><code> > 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) ≥ 0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the mean service time at center k
+(<var>S</var><code>(k) ≥ 0</code>).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the visit ratio to center k
+(<var>V</var><code>(k) ≥ 0</code>).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center k. 
+This function only supports M/M/1 queues, therefore
+<var>m</var> must be <code>ones(size(S))</code>.
+
+          <br><dt><var>Z</var><dd>external delay (think time, <var>Z</var><code> ≥ 0</code>). Default 0.
+
+        </dl>
+
+        <p><strong>OUTPUTS</strong>
+
+          <dl>
+<dt><var>Xl</var><dt><var>Xu</var><dd>Lower and upper bounds on the system throughput.
+
+          <br><dt><var>Rl</var><dt><var>Ru</var><dd>Lower and upper bounds on the system response time.
+
+        </dl>
+
+        <pre class="sp">
+     
+     </pre>
+     <strong>See also:</strong> qncsaba, qbcsbsb, qncsgb.
+
+        </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–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-367"></a><a name="index-Lam_002c-S_002e-368"></a><a name="index-Casale_002c-G_002e-369"></a><a name="index-Muntz_002c-R_002e-R_002e-370"></a><a name="index-Serazzi_002c-G_002e-371"></a>
+<a name="doc_002dqncsgb"></a>
+
+<div class="defun">
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>, <var>Ql</var>, <var>Qu</var>] = <b>qncsgb</b> (<var>N, D</var>)<var><a name="index-qncsgb-372"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>, <var>Ql</var>, <var>Qu</var>] = <b>qncsgb</b> (<var>N, S, V</var>)<var><a name="index-qncsgb-373"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>, <var>Ql</var>, <var>Qu</var>] = <b>qncsgb</b> (<var>N, S, V, m</var>)<var><a name="index-qncsgb-374"></a></var><br>
+— Function File: [<var>Xl</var>, <var>Xu</var>, <var>Rl</var>, <var>Ru</var>, <var>Ql</var>, <var>Qu</var>] = <b>qncsgb</b> (<var>N, S, V, m, Z</var>)<var><a name="index-qncsgb-375"></a></var><br>
+<blockquote>
+        <p><a name="index-bounds_002c-geometric-376"></a><a name="index-geometric-bounds-377"></a><a name="index-closed-network-378"></a>
+Compute Geometric Bounds (GB) on system throughput, system response time and server queue lenghts for closed, single-class networks.
+
+        <p><strong>INPUTS</strong>
+
+          <dl>
+<dt><var>N</var><dd>number of requests in the system (scalar, <var>N</var><code> > 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) ≥ 0</code>).
+
+          <br><dt><var>S</var><dd><var>S</var><code>(k)</code> is the mean service time at center k
+(<var>S</var><code>(k) ≥ 0</code>).
+
+          <br><dt><var>V</var><dd><var>V</var><code>(k)</code> is the visit ratio to center k
+(<var>V</var><code>(k) ≥ 0</code>).
+
+          <br><dt><var>m</var><dd><var>m</var><code>(k)</code> is the number of servers at center k. 
+This function only supports M/M/1 queues, therefore
+<var>m</var> must be <code>ones(size(S))</code>.
+
+          <br><dt><var>Z</var><dd>external delay (think time, <var>Z</var><code> ≥ 0</code>). Default 0.
+
+        </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>>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>Rl</var><dt><var>Ru</var><dd>Lower and upper bound on the system response time. These bounds
+are derived from <var>Xl</var> and <var>Xu</var> using Little's Law:
+<var>Rl</var><code> = </code><var>N</var><code> / </code><var>Xu</var><code> - </code><var>Z</var>,
+<var>Ru</var><code> = </code><var>N</var><code> / </code><var>Xl</var><code> - </code><var>Z</var>
+
+          <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>
+
+        </blockquote></div>
+
+<p class="noindent"><strong>REFERENCES</strong>
+
+<p class="noindent">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">10.1109/TC.2008.37</a>
+
+   <p><a name="index-Casale_002c-G_002e-379"></a><a name="index-Muntz_002c-R_002e-R_002e-380"></a><a name="index-Serazzi_002c-G_002e-381"></a>
+In this implementation we set X^+ and X^- as the upper
+and lower Asymptotic Bounds as computed by the <samp><span class="command">qncsab</span></samp>
+function, respectively.
+
+<!-- Examples -->
+<div class="node">
+<a name="QN-Analysis-Examples"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Bounds-Analysis">Bounds Analysis</a>,
+Up: <a rel="up" accesskey="u" href="#Queueing-Networks">Queueing Networks</a>
+
+</div>
+
+<h3 class="section">5.6 QN Analysis Examples</h3>
+
+<p>In this section we illustrate with a few examples how the
+<code>queueing</code> package can be used to evaluate queueing network
+models. Further examples can be found in the demo blocks of the
+functions described in this section, and can be accessed with the
+<code>demo </code><em>function</em> Octave command.
+
+<h4 class="subsection">5.6.1 Closed, Single Class Network</h4>
+
+<p>We now give a simple example on how the queueing package can be used
+to analyze a closed network. Let us consider again the network shown
+in <a href="#fig_003aqn_005fclosed_005fsingle">Figure 5.1</a>. We denote with S_k the average
+service time at center k, k=1, 2, 3. We use 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> \bf P. Specifically, a request completing service at
+center i is enqueued at center j with probability
+P_i, j.  We use the following routing matrix:
+
+<pre class="example">         / 0  0.3  0.7 \
+     P = | 1  0    0   |
+         \ 1  0    0   /
+</pre>
+   <p>The network above can be analyzed with the <samp><span class="command">qnclosed</span></samp> function
+see <a href="#doc_002dqnclosed">doc-qnclosed</a>. <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>We can compute V_k from the routing probability matrix
+P_i, j using the <samp><span class="command">qncsvisits</span></samp> function
+see <a href="#doc_002dqncsvisits">doc-qncsvisits</a>.  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 = qncsvisits(P);</kbd>
+     <kbd>[U R Q X] = qnclosed( N, S, V )</kbd>
+        ⇒ U = 0.99139 0.59483 0.55518
+        ⇒ R = 7.4360  4.7531  1.7500
+        ⇒ Q = 7.3719  1.4136  1.2144
+        ⇒ 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
+see <a href="#doc_002dqnsolve">doc-qnsolve</a>. <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–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>
+        ⇒ U = 0.99139 0.59483 0.55518
+        ⇒ R = 7.4360  4.7531  1.7500
+        ⇒ Q = 7.3719  1.4136  1.2144
+        ⇒ X = 0.99139 0.29742 0.69397
+</pre>
+   <h4 class="subsection">5.6.2 Open, Single Class Network</h4>
+
+<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. We use the
+<samp><span class="command">qnosvisits</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 = qnosvisits(P, lambda)</kbd>
+        ⇒ 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. 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 see <a href="#doc_002dqnopen">doc-qnopen</a>, as follows:
+
+<pre class="example">     <kbd>S = [1 2 0.8];</kbd>
+     <kbd>[U R Q X] = qnopen( sum(lambda), S, V )</kbd>
+        ⇒ U = 0.75000 0.45000 0.30000
+        ⇒ R = 4.0000  3.6364  1.1429
+        ⇒ Q = 3.00000 0.81818 0.42857
+        ⇒ 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>
+        ⇒ U = 0.75000 0.45000 0.30000
+        ⇒ R = 4.0000  3.6364  1.1429
+        ⇒ Q = 3.00000 0.81818 0.42857
+        ⇒ X = 0.75000 0.22500 0.37500
+</pre>
+   <h4 class="subsection">5.6.3 Closed Multiclass Network/1</h4>
+
+<p>The following example is taken from Herb Schwetman, <cite>Implementing
+the Mean Value Algorith for the Solution of Queueing Network Models</cite>,
+Technical Report CSD-TR-355, Department of Computer Sciences, Purdue
+University, feb 15, 1982.
+
+   <p>We consider the following multiclass QN with three servers and two classes
+
+   <div class="float">
+<a name="fig_003aapl"></a><div align="center"><img src="qn_closed_multi_apl.png" alt="qn_closed_multi_apl.png"></div>
+   <p><strong class="float-caption">Figure 5.3</strong></p></div>
+
+   <p>Servers 1 and 2 (labeled <em>APL</em> and <em>IMS</em>, respectively) are
+infinite server nodes; server 3 (labeled <em>SYS</em>) is Processor
+Sharing (PS). Mean service times are given in the following table:
+
+   <p><table summary=""><tr align="left"><th valign="top" width="15%"></th><th valign="top" width="15%">APL </th><th valign="top" width="15%">IMS </th><th valign="top" width="15%">SYS
+<br></th></tr><tr align="left"><td valign="top" width="15%">Class 1 </td><td valign="top" width="15%">1 </td><td valign="top" width="15%">- </td><td valign="top" width="15%">0.025
+<br></td></tr><tr align="left"><td valign="top" width="15%">Class 2 </td><td valign="top" width="15%">- </td><td valign="top" width="15%">15 </td><td valign="top" width="15%">0.500
+   <br></td></tr></table>
+
+   <p>There is no class switching. If  we assume a population of 15 requests
+for  class 1,  and  5 requests  for class  2,  then the  model can  be
+analyzed as follows:
+
+<pre class="example"><pre class="verbatim">      S = [1 0 .025; 0 15 .5];
+      P = zeros(2,3,2,3);
+      P(1,1,1,3) = P(1,3,1,1) = 1;
+      P(2,2,2,3) = P(2,3,2,2) = 1;
+      V = qncmvisits(P,[3 3]); # reference station is station 3
+      N = [15 5];
+      m = [-1 -1 1];
+      [U R Q X] = qncmmva(N,S,V,m)
+</pre>
+       ⇒
+     U =
+     
+        14.32312    0.00000    0.35808
+         0.00000    4.70699    0.15690
+     
+     R =
+     
+         1.00000    0.00000    0.04726
+         0.00000   15.00000    0.93374
+     
+     Q =
+     
+        14.32312    0.00000    0.67688
+         0.00000    4.70699    0.29301
+     
+     X =
+     
+        14.32312    0.00000   14.32312
+         0.00000    0.31380    0.31380
+</pre>
+   <h4 class="subsection">5.6.4 Closed Multiclass Network/2</h4>
+
+<p>The following example is taken from M. Marzolla, <cite>The qnetworks
+Toolbox: A Software Package for Queueing Networks Analysis</cite>, Technical
+Report
+<a href="http://www.informatica.unibo.it/ricerca/technical-report/2010/UBLCS-2010-04">UBLCS-2010-04</a>, Department of Computer Science, University of Bologna,
+Italy, February 2010.
+
+   <div class="float">
+<a name="fig_003aweb_005fmodel"></a><div align="center"><img src="qn_web_model.png" alt="qn_web_model.png"></div>
+   <p><strong class="float-caption">Figure 5.4: Three-tier enterprise system model</strong></p></div>
+
+   <p>The model shown in <a href="#fig_003aweb_005fmodel">Figure 5.4</a> shows a three-tier enterprise system
+with K=6 service centers. The first tier contains the
+<em>Web server</em> (node 1), which is responsible for generating Web pages and
+transmitting them to clients. The application logic is implemented by
+nodes 2 and 3, and the storage tier is made of nodes 4–6.The system
+is subject to two workload classes, both represented as closed
+populations of N_1 and N_2 requests, respectively. Let
+D_c, k denote the service demand of class c requests
+at center k. We use the parameter values:
+
+   <p><table summary=""><tr align="left"><th valign="top" width="20%">Serv. no. </th><th valign="top" width="33%">Name </th><th valign="top" width="10%">Class 1 </th><th valign="top" width="10%">Class 2
+<br></th></tr><tr align="left"><td valign="top" width="20%">1 </td><td valign="top" width="33%">Web Server    </td><td valign="top" width="10%">12 </td><td valign="top" width="10%">2
+<br></td></tr><tr align="left"><td valign="top" width="20%">2 </td><td valign="top" width="33%">App. Server 1 </td><td valign="top" width="10%">14 </td><td valign="top" width="10%">20
+<br></td></tr><tr align="left"><td valign="top" width="20%">3 </td><td valign="top" width="33%">App. Server 2 </td><td valign="top" width="10%">23 </td><td valign="top" width="10%">14
+<br></td></tr><tr align="left"><td valign="top" width="20%">4 </td><td valign="top" width="33%">DB Server 1   </td><td valign="top" width="10%">20 </td><td valign="top" width="10%">90
+<br></td></tr><tr align="left"><td valign="top" width="20%">5 </td><td valign="top" width="33%">DB Server 2   </td><td valign="top" width="10%">80 </td><td valign="top" width="10%">30
+<br></td></tr><tr align="left"><td valign="top" width="20%">6 </td><td valign="top" width="33%">DB Server 3   </td><td valign="top" width="10%">31 </td><td valign="top" width="10%">33
+   <br></td></tr></table>
+
+   <p>We set the total number of requests to 100, that is N_1 + N_2 =
+N = 100, and we study how different population mixes (N_1,
+N_2) affect the system throughput and response time. Let
+\beta_1 \in (0, 1) denote the fraction of class 1 requests:
+N_1 = \beta_1 N, N_2 = (1-\beta_1)N. The following
+Octave code defines the model for \beta_1 = 0.1:
+
+<pre class="example">     N = 100;     # total population size
+     beta1 = 0.1; # fraction of class 1 reqs.
+     S = [12 14 23 20 80 31; \
+           2 20 14 90 30 33 ];
+     V = ones(size(S));
+     pop = [fix(beta1*N) N-fix(beta1*N)];
+     [U R Q X] = qncmmva(pop, S, V);
+</pre>
+   <p>The <samp><span class="command">qncmmva(pop, S, V)</span></samp> function invocation (line 7)
+uses the multiclass MVA algorithm to compute per-class utilizations
+U_c, k, response times R_c,k, mean queue lengths
+Q_c,k and throughputs X_c,k at each service center
+k, given a population vector <var>pop</var>, mean service times
+<var>S</var> and visit ratios <var>V</var>. Since we are given the service
+demands D_c, k = S_c, k V_c,k, but function
+<samp><span class="command">qncmmva()</span></samp> requires separate service times and visit
+ratios, we set the service times equal to the demands (line 3–4), and
+all visit ratios equal to one (line 5). Overall class and system
+throughputs and response times can also be computed:
+
+<pre class="example">     X1 = X(1,1) / V(1,1)     # class 1 throughput
+             ⇒ X1 =  0.0044219
+     X2 = X(2,1) / V(2,1)     # class 2 throughput
+             ⇒ X2 =  0.010128
+     XX = X1 + X2             # system throughput
+             ⇒ XX =  0.014550
+     R1 = dot(R(1,:), V(1,:)) # class 1 resp. time
+             ⇒ R1 =  2261.5
+     R2 = dot(R(2,:), V(2,:)) # class 2 resp. time
+             ⇒ R2 =  8885.9
+     RR = N / XX              # system resp. time
+             ⇒ RR =  6872.7
+</pre>
+   <p><code>dot(X,Y)</code> computes the dot product of two vectors. 
+<code>R(1,:)</code> is the first row of matrix <var>R</var> and <code>V(1,:)</code> is
+the first row of matrix <var>V</var>, so <code>dot(R(1,:), V(1,:))</code>
+computes \sum_k R_1,k V_1,k.
+
+   <div class="float">
+<a name="fig_003aweb"></a><div align="center"><img src="web.png" alt="web.png"></div>
+   <p><strong class="float-caption">Figure 5.5: Throughput and Response Times as a function of the population mix</strong></p></div>
+
+   <p>We can also compute the system power \Phi = X / R, which
+defines how efficiently resources are being used: high values of
+\Phi denote the desirable situation of high throughput and low
+response time. <a href="#fig_003apower">fig:power</a> shows \Phi as a function of
+\beta_1. We observe a “plateau” of the global system power,
+corresponding to values of \beta_1 which approximately lie
+between 0.3 and 0.7. The per-class power exhibits an
+interesting (although not completely surprising) pattern, where the
+class with higher population exhibits worst efficiency as it produces
+higher contention on the resources.
+
+   <div class="float">
+<a name="fig_003apower"></a><div align="center"><img src="power.png" alt="power.png"></div>
+   <p><strong class="float-caption">Figure 5.6: System Power as a function of the population mix</strong></p></div>
+
+<h4 class="subsection">5.6.5 Closed Multiclass Network/3</h4>
+
+<p>We now consider an example of multiclass network with class switching. 
+The example is taken from <a href="#Sch82">Sch82</a>, and is shown in Figure
+<a href="#fig_003aclass_005fswitching">fig:class_switching</a>.
+
+   <div class="float">
+<a name="fig_003aclass_005fswitching"></a><div align="center"><img src="qn_closed_multi_cs.png" alt="qn_closed_multi_cs.png"></div>
+   <p><strong class="float-caption">Figure 5.7: Multiclass Model with Class Switching</strong></p></div>
+
+   <p>The system consists of three devices and two job classes. The CPU node
+is a PS server, while the two nodes labeled I/O are FCFS. Class 1 mean
+service time at the CPU is 0.01; class 2 mean service time at the CPU
+is 0.05. The mean service time at node 2 is 0.1, and is
+class-independent. Similarly, the mean service time at node 3 is
+0.07. Jobs in class 1 leave the CPU and join class 2 with probability
+0.1; jobs of class 2 leave the CPU and join class 1 with probability
+0.2. There are N=3 jobs, which are initially allocated to class
+1. However, note that since class switching is allowed, the total
+number of jobs in each class does not remain constant; however the
+total number of jobs does.
+
+<pre class="example"><pre class="verbatim">      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];
+      [U R Q X] = qncmmva(N, S, P)
+</pre>
+       ⇒
+     U =
+     
+        0.12609   0.61784   0.25218
+        0.31522   0.13239   0.31522
+     
+     R =
+     
+        0.014653   0.133148   0.163256
+        0.073266   0.133148   0.163256
+     
+     Q =
+     
+        0.18476   1.17519   0.41170
+        0.46190   0.25183   0.51462
+     
+     X =
+     
+        12.6089    8.8262    2.5218
+         6.3044    1.8913    3.1522
+</pre>
+   <!-- This file has been automatically generated from references.txi -->
+<!-- by proc.m. Do not edit this file, all changes will be lost -->
+<!-- *- texinfo -*- -->
+<!-- Copyright (C) 2012 Moreno Marzolla -->
+<!-- This file is part of the queueing package. -->
+<!-- The queueing package 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 package 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 package; see the file COPYING.  If not, see -->
+<!-- <http://www.gnu.org/licenses/>. -->
+<div class="node">
+<a name="References"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Copying">Copying</a>,
+Previous: <a rel="previous" accesskey="p" href="#Queueing-Networks">Queueing Networks</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="chapter">6 References</h2>
+
+     <dl>
+<dt>[Aky88]<dd>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–428.  DOI <a href="http://dx.doi.org/10.1109/32.4663">10.1109/32.4663</a>
+
+     <br><dt>[Bar79]<dd>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–62.
+
+     <br><dt>[BCMP75]<dd>F. Baskett, K. Mani Chandy, R. R. Muntz, and F. G. Palacios. 1975. <cite>Open, Closed, and Mixed Networks of Queues with Different Classes of Customers</cite>. J. ACM 22, 2 (April 1975), 248—260, DOI <a href="http://doi.acm.org/10.1145/321879.321887">10.1145/321879.321887</a>
+
+     <br><dt>[BGMT98]<dd>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.
+
+     <br><dt>[Buz73]<dd>J. 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–531. DOI <a href="http://doi.acm.org/10.1145/362342.362345">10.1145/362342.362345</a>
+
+     <br><dt>[C08]<dd>G. Casale, <cite>A note on stable flow-equivalent aggregation in
+closed networks</cite>. Queueing Syst. Theory Appl., 60:193–-202, December
+2008, DOI <a href="http://dx.doi.org/10.1007/s11134-008-9093-6">10.1007/s11134-008-9093-6</a>
+
+     <br><dt>[CMS08]<dd>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. DOI <a href="http://doi.ieeecomputersociety.org/10.1109/TC.2008.37">10.1109/TC.2008.37</a>
+
+     <br><dt><a name="GrSn97"></a>[GrSn97]<dd>C. M. Grinstead, J. L. Snell, (July 1997). <cite>Introduction
+to Probability</cite>. American Mathematical Society. ISBN 978-0821807491;
+this excellent textbook is <a href="http://www.dartmouth.edu/~chance/teaching_aids/books_articles/probability_book/amsbook.mac.pdf">available in PDF format</a>
+and can be used under the terms of the <a href="http://www.gnu.org/copyleft/fdl.html">GNU Free Documentation License (FDL)</a>
+
+     <br><dt>[Jac04]<dd>J. R. Jackson, <cite>Jobshop-Like Queueing Systems</cite>, Vol. 50, No. 12, Ten Most Influential Titles of "Management Science's" First Fifty Years (Dec., 2004), pp. 1796-1802, <a href="http://www.jstor.org/stable/30046149">available online</a>
+
+     <br><dt>[Jai91]<dd>R. Jain, <cite>The Art of Computer Systems Performance Analysis</cite>,
+Wiley, 1991, p. 577.
+
+     <br><dt>[HsLa87]<dd>C. H. Hsieh and S. Lam,
+<cite>Two classes of performance bounds for closed queueing networks</cite>,
+PEVA, vol. 7, n. 1, pp. 3–30, 1987
+
+     <br><dt>[Ker84]<dd>T. Kerola, <cite>The Composite Bound Method (CBM) for Computing
+Throughput Bounds in Multiple Class Environments</cite>,
+<a href="http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1394&context=cstech">Technical Report CSD-TR-475</a>, Department of Computer Sciences, Purdue
+University, mar 13, 1984 (Revised aug 27, 1984).
+
+     <br><dt>[LZGS84]<dd>E. D. Lazowska, J. Zahorjan, G. Scott Graham, and K. 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/">available online</a>.
+
+     <br><dt>[ReKo76]<dd>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–31,
+1976). SIGMETRICS '76. ACM, New York, NY,
+pp. 109–117. DOI <a href="http://doi.acm.org/10.1145/800200.806187">10.1145/800200.806187</a>
+
+     <br><dt>[ReLa80]<dd>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–322. DOI <a href="http://doi.acm.org/10.1145/322186.322195">10.1145/322186.322195</a>
+
+     <br><dt>[Sch79]<dd>P. Schweitzer, <cite>Approximate Analysis of Multiclass Closed Networks of
+Queues</cite>, Proc. Int. Conf. on Stochastic Control and Optimization, jun
+1979, pp. 25—29
+
+     <br><dt>[Sch81]<dd>H. Schwetman, <cite>Some Computational
+Aspects of Queueing Network Models</cite>, <a href="http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-354.pdf">Technical Report CSD-TR-354</a>,
+Department of Computer Sciences, Purdue University, feb, 1981
+(revised).
+
+     <br><dt><a name="Sch82"></a>[Sch82]<dd>H. Schwetman, <cite>Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models</cite>, <a href="http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-355.pdf">Technical Report CSD-TR-355</a>,
+Department of Computer Sciences, Purdue University, feb 15, 1982.
+
+     <br><dt>[Tij03]<dd>H. C. Tijms, <cite>A first course in stochastic models</cite>,
+John Wiley and Sons, 2003, ISBN 0471498807, ISBN 9780471498803,
+DOI <a href="http://dx.doi.org/10.1002/047001363X">10.1002/047001363X</a>
+
+     <br><dt>[ZaWo81]<dd>J. Zahorjan and E. Wong, <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">10.1145/1010629.805477</a>
+
+     <br><dt>[Zeng03]<dd>G. Zeng, <cite>Two common properties of the erlang-B function, erlang-C function, and Engset blocking function</cite>, Mathematical and Computer Modelling, Volume 37, Issues 12-13, June 2003, Pages 1287-1296 DOI
+<a href="http://dx.doi.org/10.1016/S0895-7177(03)90040-9">10.1016/S0895-7177(03)90040-9</a>
+
+</dl>
+
+<!-- Appendix starts here -->
+<!-- This file has been automatically generated from gpl.txi -->
+<!-- by proc.m. Do not edit this file, all changes will be lost -->
+<div class="node">
+<a name="Copying"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Concept-Index">Concept Index</a>,
+Previous: <a rel="previous" accesskey="p" href="#References">References</a>,
+Up: <a rel="up" accesskey="u" href="#Top">Top</a>
+
+</div>
+
+<h2 class="appendix">Appendix A GNU GENERAL PUBLIC LICENSE</h2>
+
+<p><a name="index-warranty-382"></a><a name="index-copyright-383"></a>
+<div align="center">Version 3, 29 June 2007</div>
+
+<pre class="display">     Copyright © 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—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>“This License” refers to version 3 of the GNU General Public License.
+
+     <p>“Copyright” also means copyright-like laws that apply to other kinds
+of works, such as semiconductor masks.
+
+     <p>“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.
+
+     <p>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.
+
+     <p>A “covered work” means either the unmodified Program or a work based
+on the Program.
+
+     <p>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.
+
+     <p>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.
+
+     <p>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.
+
+     <li>Source Code.
+
+     <p>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.
+
+     <p>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.
+
+     <p>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.
+
+     <p>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.
+
+     <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 “keep intact all
+notices”.
+
+          <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
+“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.
+
+     <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 “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.
+
+     <p>“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.
+
+     <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>“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.
+
+     <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 “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.
+
+     <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 “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.
+
+     <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 “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”.
+
+     <p>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.
+
+     <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 “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.
+
+     <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.  “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.
+
+     <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 “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.
+
+     <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 “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.
+
+     <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 “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.
+
+     <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 “copyright” 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 ‘<samp><span class="samp">show w</span></samp>’.
+     This is free software, and you are welcome to redistribute it
+     under certain conditions; type ‘<samp><span class="samp">show c</span></samp>’ for details.
+</pre>
+   <p>The hypothetical commands ‘<samp><span class="samp">show w</span></samp>’ and ‘<samp><span class="samp">show c</span></samp>’ 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”.
+
+   <p>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
+<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: <a rel="next" accesskey="n" href="#Function-Index">Function Index</a>,
+Previous: <a rel="previous" accesskey="p" href="#Copying">Copying</a>,
+Up: <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-absorption-probabilities_002c-DTMC-33">absorption probabilities, DTMC</a>: <a href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a></li>
+<li><a href="#index-approximate-MVA-177">approximate MVA</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-asymmetric-_0040math_007bM_002fM_002fm_007d-system-119">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-asymptotic-bounds-317">asymptotic bounds</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-balanced-system-bounds-342">balanced system bounds</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-BCMP-network-135">BCMP network</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-birth_002ddeath-process_002c-CTMC-57">birth-death process, CTMC</a>: <a href="#Birth_002ddeath-process-_0028CTMC_0029">Birth-death process (CTMC)</a></li>
+<li><a href="#index-birth_002ddeath-process_002c-DTMC-17">birth-death process, DTMC</a>: <a href="#Birth_002ddeath-process-_0028DTMC_0029">Birth-death process (DTMC)</a></li>
+<li><a href="#index-blocking-queueing-network-208">blocking queueing network</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-bounds_002c-asymptotic-305">bounds, asymptotic</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-bounds_002c-balanced-system-334">bounds, balanced system</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-bounds_002c-composite-357">bounds, composite</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-bounds_002c-geometric-376">bounds, geometric</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-bounds_002c-PB-364">bounds, PB</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-closed-multiclass-network-327">closed multiclass network</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-closed-network-325">closed network</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-closed-network-195">closed network</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-closed-network_002c-approximate-analysis-179">closed network, approximate analysis</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-closed-network_002c-finite-capacity-209">closed network, finite capacity</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-closed-network_002c-multiple-classes-299">closed network, multiple classes</a>: <a href="#Generic-Algorithms">Generic Algorithms</a></li>
+<li><a href="#index-closed-network_002c-multiple-classes-235">closed network, multiple classes</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-closed-network_002c-multiple-classes-216">closed network, multiple classes</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-closed-network_002c-single-class-318">closed network, single class</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-closed-network_002c-single-class-298">closed network, single class</a>: <a href="#Generic-Algorithms">Generic Algorithms</a></li>
+<li><a href="#index-closed-network_002c-single-class-144">closed network, single class</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-CMVA-168">CMVA</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-composite-bounds-358">composite bounds</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-conditional-MVA-_0028CMVA_0029-165">conditional MVA (CMVA)</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-continuous-time-Markov-chain-81">continuous time Markov chain</a>: <a href="#First-passage-times-_0028CTMC_0029">First passage times (CTMC)</a></li>
+<li><a href="#index-continuous-time-Markov-chain-70">continuous time Markov chain</a>: <a href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a></li>
+<li><a href="#index-continuous-time-Markov-chain-66">continuous time Markov chain</a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028CTMC_0029">Time-averaged expected sojourn times (CTMC)</a></li>
+<li><a href="#index-continuous-time-Markov-chain-55">continuous time Markov chain</a>: <a href="#Birth_002ddeath-process-_0028CTMC_0029">Birth-death process (CTMC)</a></li>
+<li><a href="#index-continuous-time-Markov-chain-49">continuous time Markov chain</a>: <a href="#State-occupancy-probabilities-_0028CTMC_0029">State occupancy probabilities (CTMC)</a></li>
+<li><a href="#index-convolution-algorithm-188">convolution algorithm</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-copyright-383">copyright</a>: <a href="#Copying">Copying</a></li>
+<li><a href="#index-CTMC-80">CTMC</a>: <a href="#First-passage-times-_0028CTMC_0029">First passage times (CTMC)</a></li>
+<li><a href="#index-CTMC-71">CTMC</a>: <a href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a></li>
+<li><a href="#index-CTMC-67">CTMC</a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028CTMC_0029">Time-averaged expected sojourn times (CTMC)</a></li>
+<li><a href="#index-CTMC-56">CTMC</a>: <a href="#Birth_002ddeath-process-_0028CTMC_0029">Birth-death process (CTMC)</a></li>
+<li><a href="#index-CTMC-52">CTMC</a>: <a href="#State-occupancy-probabilities-_0028CTMC_0029">State occupancy probabilities (CTMC)</a></li>
+<li><a href="#index-deprecated-functions-1">deprecated functions</a>: <a href="#Naming-Conventions">Naming Conventions</a></li>
+<li><a href="#index-discrete-time-Markov-chain-41">discrete time Markov chain</a>: <a href="#First-passage-times-_0028DTMC_0029">First passage times (DTMC)</a></li>
+<li><a href="#index-discrete-time-Markov-chain-36">discrete time Markov chain</a>: <a href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a></li>
+<li><a href="#index-discrete-time-Markov-chain-27">discrete time Markov chain</a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028DTMC_0029">Time-averaged expected sojourn times (DTMC)</a></li>
+<li><a href="#index-discrete-time-Markov-chain-22">discrete time Markov chain</a>: <a href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a></li>
+<li><a href="#index-discrete-time-Markov-chain-16">discrete time Markov chain</a>: <a href="#Birth_002ddeath-process-_0028DTMC_0029">Birth-death process (DTMC)</a></li>
+<li><a href="#index-discrete-time-Markov-chain-9">discrete time Markov chain</a>: <a href="#State-occupancy-probabilities-_0028DTMC_0029">State occupancy probabilities (DTMC)</a></li>
+<li><a href="#index-discrete-time-Markov-chain-5">discrete time Markov chain</a>: <a href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a></li>
+<li><a href="#index-DTMC-43">DTMC</a>: <a href="#First-passage-times-_0028DTMC_0029">First passage times (DTMC)</a></li>
+<li><a href="#index-DTMC-35">DTMC</a>: <a href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a></li>
+<li><a href="#index-DTMC-29">DTMC</a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028DTMC_0029">Time-averaged expected sojourn times (DTMC)</a></li>
+<li><a href="#index-DTMC-21">DTMC</a>: <a href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a></li>
+<li><a href="#index-DTMC-15">DTMC</a>: <a href="#Birth_002ddeath-process-_0028DTMC_0029">Birth-death process (DTMC)</a></li>
+<li><a href="#index-DTMC-10">DTMC</a>: <a href="#State-occupancy-probabilities-_0028DTMC_0029">State occupancy probabilities (DTMC)</a></li>
+<li><a href="#index-DTMC-4">DTMC</a>: <a href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a></li>
+<li><a href="#index-Engset-loss-formula-102">Engset loss formula</a>: <a href="#The-Engset-Formula">The Engset Formula</a></li>
+<li><a href="#index-Erlang_002dB-formula-97">Erlang-B formula</a>: <a href="#The-Erlang_002dB-Formula">The Erlang-B Formula</a></li>
+<li><a href="#index-Erlang_002dC-formula-100">Erlang-C formula</a>: <a href="#The-Erlang_002dC-Formula">The Erlang-C Formula</a></li>
+<li><a href="#index-expected-sojourn-time_002c-CTMC-61">expected sojourn time, CTMC</a>: <a href="#Expected-sojourn-times-_0028CTMC_0029">Expected sojourn times (CTMC)</a></li>
+<li><a href="#index-expected-sojourn-times_002c-DTMC-20">expected sojourn times, DTMC</a>: <a href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a></li>
+<li><a href="#index-first-passage-times-39">first passage times</a>: <a href="#First-passage-times-_0028DTMC_0029">First passage times (DTMC)</a></li>
+<li><a href="#index-first-passage-times_002c-CTMC-79">first passage times, CTMC</a>: <a href="#First-passage-times-_0028CTMC_0029">First passage times (CTMC)</a></li>
+<li><a href="#index-fundamental-matrix-34">fundamental matrix</a>: <a href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a></li>
+<li><a href="#index-geometric-bounds-377">geometric bounds</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-load_002ddependent-service-center-158">load-dependent service center</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-g_t_0040math_007bM_002fG_002f1_007d-system-125">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-127">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-84">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-111">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-104">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-91">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-113">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-82">Markov chain, continuous time</a>: <a href="#First-passage-times-_0028CTMC_0029">First passage times (CTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-69">Markov chain, continuous time</a>: <a href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-64">Markov chain, continuous time</a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028CTMC_0029">Time-averaged expected sojourn times (CTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-60">Markov chain, continuous time</a>: <a href="#Expected-sojourn-times-_0028CTMC_0029">Expected sojourn times (CTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-54">Markov chain, continuous time</a>: <a href="#Birth_002ddeath-process-_0028CTMC_0029">Birth-death process (CTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-48">Markov chain, continuous time</a>: <a href="#State-occupancy-probabilities-_0028CTMC_0029">State occupancy probabilities (CTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-continuous-time-45">Markov chain, continuous time</a>: <a href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a></li>
+<li><a href="#index-Markov-chain_002c-discrete-time-42">Markov chain, discrete time</a>: <a href="#First-passage-times-_0028DTMC_0029">First passage times (DTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-discrete-time-37">Markov chain, discrete time</a>: <a href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-discrete-time-28">Markov chain, discrete time</a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028DTMC_0029">Time-averaged expected sojourn times (DTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-discrete-time-23">Markov chain, discrete time</a>: <a href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-discrete-time-14">Markov chain, discrete time</a>: <a href="#Birth_002ddeath-process-_0028DTMC_0029">Birth-death process (DTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-discrete-time-8">Markov chain, discrete time</a>: <a href="#State-occupancy-probabilities-_0028DTMC_0029">State occupancy probabilities (DTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-discrete-time-3">Markov chain, discrete time</a>: <a href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a></li>
+<li><a href="#index-Markov-chain_002c-state-occupancy-probabilities-50">Markov chain, state occupancy probabilities</a>: <a href="#State-occupancy-probabilities-_0028CTMC_0029">State occupancy probabilities (CTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-stationary-probabilities-11">Markov chain, stationary probabilities</a>: <a href="#State-occupancy-probabilities-_0028DTMC_0029">State occupancy probabilities (DTMC)</a></li>
+<li><a href="#index-Markov-chain_002c-transient-probabilities-12">Markov chain, transient probabilities</a>: <a href="#State-occupancy-probabilities-_0028DTMC_0029">State occupancy probabilities (DTMC)</a></li>
+<li><a href="#index-mean-recurrence-times-40">mean recurrence times</a>: <a href="#First-passage-times-_0028DTMC_0029">First passage times (DTMC)</a></li>
+<li><a href="#index-mean-time-to-absorption_002c-CTMC-72">mean time to absorption, CTMC</a>: <a href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a></li>
+<li><a href="#index-mean-time-to-absorption_002c-DTMC-32">mean time to absorption, DTMC</a>: <a href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a></li>
+<li><a href="#index-Mean-Value-Analysis_002c-conditional-_0028CMVA_0029-166">Mean Value Analysis, conditional (CMVA)</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Mean-Value-Analysys-_0028MVA_0029-250">Mean Value Analysys (MVA)</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Mean-Value-Analysys-_0028MVA_0029-143">Mean Value Analysys (MVA)</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Mean-Value-Analysys-_0028MVA_0029_002c-approximate-268">Mean Value Analysys (MVA), approximate</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Mean-Value-Analysys-_0028MVA_0029_002c-approximate-175">Mean Value Analysys (MVA), approximate</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-mixed-network-280">mixed network</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-multiclass-network_002c-closed-326">multiclass network, closed</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-multiclass-network_002c-closed-252">multiclass network, closed</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-multiclass-network_002c-open-311">multiclass network, open</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-multiclass-network_002c-open-228">multiclass network, open</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-MVA-156">MVA</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-MVA_002c-approximate-269">MVA, approximate</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-MVA_002c-approximate-176">MVA, approximate</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-MVABLO-210">MVABLO</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-normalization-constant-145">normalization constant</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-open-network-306">open network</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-open-network-301">open network</a>: <a href="#Generic-Algorithms">Generic Algorithms</a></li>
+<li><a href="#index-open-network_002c-multiple-classes-227">open network, multiple classes</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-open-network_002c-single-class-134">open network, single class</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-PB-bounds-365">PB bounds</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-population-mix-234">population mix</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-queueing-network-with-blocking-207">queueing network with blocking</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-queueing-networks-128">queueing networks</a>: <a href="#Queueing-Networks">Queueing Networks</a></li>
+<li><a href="#index-RS-blocking-219">RS blocking</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-stationary-probabilities-51">stationary probabilities</a>: <a href="#State-occupancy-probabilities-_0028CTMC_0029">State occupancy probabilities (CTMC)</a></li>
+<li><a href="#index-time_002dalveraged-sojourn-time_002c-CTMC-65">time-alveraged sojourn time, CTMC</a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028CTMC_0029">Time-averaged expected sojourn times (CTMC)</a></li>
+<li><a href="#index-time_002dalveraged-sojourn-time_002c-DTMC-26">time-alveraged sojourn time, DTMC</a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028DTMC_0029">Time-averaged expected sojourn times (DTMC)</a></li>
+<li><a href="#index-traffic-intensity-105">traffic intensity</a>: <a href="#The-M_002fM_002finf-System">The M/M/inf System</a></li>
+<li><a href="#index-warranty-382">warranty</a>: <a href="#Copying">Copying</a></li>
+   </ul><div class="node">
+<a name="Function-Index"></a>
+<p><hr>
+Next: <a rel="next" accesskey="n" href="#Author-Index">Author Index</a>,
+Previous: <a rel="previous" accesskey="p" href="#Concept-Index">Concept Index</a>,
+Up: <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-46"><code>ctmc</code></a>: <a href="#State-occupancy-probabilities-_0028CTMC_0029">State occupancy probabilities (CTMC)</a></li>
+<li><a href="#index-ctmcbd-53"><code>ctmcbd</code></a>: <a href="#Birth_002ddeath-process-_0028CTMC_0029">Birth-death process (CTMC)</a></li>
+<li><a href="#index-ctmcchkQ-44"><code>ctmcchkQ</code></a>: <a href="#Continuous_002dTime-Markov-Chains">Continuous-Time Markov Chains</a></li>
+<li><a href="#index-ctmcexps-58"><code>ctmcexps</code></a>: <a href="#Expected-sojourn-times-_0028CTMC_0029">Expected sojourn times (CTMC)</a></li>
+<li><a href="#index-ctmcfpt-77"><code>ctmcfpt</code></a>: <a href="#First-passage-times-_0028CTMC_0029">First passage times (CTMC)</a></li>
+<li><a href="#index-ctmcmtta-68"><code>ctmcmtta</code></a>: <a href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a></li>
+<li><a href="#index-ctmctaexps-62"><code>ctmctaexps</code></a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028CTMC_0029">Time-averaged expected sojourn times (CTMC)</a></li>
+<li><a href="#index-dtmc-6"><code>dtmc</code></a>: <a href="#State-occupancy-probabilities-_0028DTMC_0029">State occupancy probabilities (DTMC)</a></li>
+<li><a href="#index-dtmcbd-13"><code>dtmcbd</code></a>: <a href="#Birth_002ddeath-process-_0028DTMC_0029">Birth-death process (DTMC)</a></li>
+<li><a href="#index-dtmcchkP-2"><code>dtmcchkP</code></a>: <a href="#Discrete_002dTime-Markov-Chains">Discrete-Time Markov Chains</a></li>
+<li><a href="#index-dtmcexps-18"><code>dtmcexps</code></a>: <a href="#Expected-number-of-visits-_0028DTMC_0029">Expected number of visits (DTMC)</a></li>
+<li><a href="#index-dtmcfpt-38"><code>dtmcfpt</code></a>: <a href="#First-passage-times-_0028DTMC_0029">First passage times (DTMC)</a></li>
+<li><a href="#index-dtmcmtta-30"><code>dtmcmtta</code></a>: <a href="#Mean-time-to-absorption-_0028DTMC_0029">Mean time to absorption (DTMC)</a></li>
+<li><a href="#index-dtmctaexps-24"><code>dtmctaexps</code></a>: <a href="#Time_002daveraged-expected-sojourn-times-_0028DTMC_0029">Time-averaged expected sojourn times (DTMC)</a></li>
+<li><a href="#index-engset-101"><code>engset</code></a>: <a href="#The-Engset-Formula">The Engset Formula</a></li>
+<li><a href="#index-erlangb-96"><code>erlangb</code></a>: <a href="#The-Erlang_002dB-Formula">The Erlang-B Formula</a></li>
+<li><a href="#index-erlangc-99"><code>erlangc</code></a>: <a href="#The-Erlang_002dC-Formula">The Erlang-C Formula</a></li>
+<li><a href="#index-qnclosed-297"><code>qnclosed</code></a>: <a href="#Generic-Algorithms">Generic Algorithms</a></li>
+<li><a href="#index-qncmaba-319"><code>qncmaba</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qncmbsb-343"><code>qncmbsb</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qncmcb-353"><code>qncmcb</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qncmmva-243"><code>qncmmva</code></a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-qncmmvaap-263"><code>qncmmvaap</code></a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-qncmnpop-238"><code>qncmnpop</code></a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-qncmpopmix-233"><code>qncmpopmix</code></a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-qncmvisits-220"><code>qncmvisits</code></a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-qncsaba-312"><code>qncsaba</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qncsbsb-336"><code>qncsbsb</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qncscmva-163"><code>qncscmva</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qncsconv-184"><code>qncsconv</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qncsconvld-194"><code>qncsconvld</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qncsgb-372"><code>qncsgb</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qncsmva-140"><code>qncsmva</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qncsmvaap-170"><code>qncsmvaap</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qncsmvablo-206"><code>qncsmvablo</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qncsmvald-153"><code>qncsmvald</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qncspb-360"><code>qncspb</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qncsvisits-129"><code>qncsvisits</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qnmarkov-212"><code>qnmarkov</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qnmix-278"><code>qnmix</code></a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-qnmknode-286"><code>qnmknode</code></a>: <a href="#Generic-Algorithms">Generic Algorithms</a></li>
+<li><a href="#index-qnom-223"><code>qnom</code></a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-qnomaba-307"><code>qnomaba</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qnomvisits-222"><code>qnomvisits</code></a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-qnopen-300"><code>qnopen</code></a>: <a href="#Generic-Algorithms">Generic Algorithms</a></li>
+<li><a href="#index-qnos-132"><code>qnos</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qnosaba-302"><code>qnosaba</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qnosbsb-332"><code>qnosbsb</code></a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-qnosvisits-131"><code>qnosvisits</code></a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-qnsolve-293"><code>qnsolve</code></a>: <a href="#Generic-Algorithms">Generic Algorithms</a></li>
+<li><a href="#index-qsammm-118"><code>qsammm</code></a>: <a href="#The-Asymmetric-M_002fM_002fm-System">The Asymmetric M/M/m System</a></li>
+<li><a href="#index-qsmg1-124"><code>qsmg1</code></a>: <a href="#The-M_002fG_002f1-System">The M/G/1 System</a></li>
+<li><a href="#index-qsmh1-126"><code>qsmh1</code></a>: <a href="#The-M_002fHm_002f1-System">The M/Hm/1 System</a></li>
+<li><a href="#index-qsmm1-83"><code>qsmm1</code></a>: <a href="#The-M_002fM_002f1-System">The M/M/1 System</a></li>
+<li><a href="#index-qsmm1k-110"><code>qsmm1k</code></a>: <a href="#The-M_002fM_002f1_002fK-System">The M/M/1/K System</a></li>
+<li><a href="#index-qsmminf-103"><code>qsmminf</code></a>: <a href="#The-M_002fM_002finf-System">The M/M/inf System</a></li>
+<li><a href="#index-qsmmm-89"><code>qsmmm</code></a>: <a href="#The-M_002fM_002fm-System">The M/M/m System</a></li>
+<li><a href="#index-qsmmmk-112"><code>qsmmmk</code></a>: <a href="#The-M_002fM_002fm_002fK-System">The M/M/m/K System</a></li>
+   </ul><div class="node">
+<a name="Author-Index"></a>
+<p><hr>
+Previous: <a rel="previous" accesskey="p" href="#Function-Index">Function Index</a>,
+Up: <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-211">Akyildiz, I. F.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Bard_002c-Y_002e-272">Bard, Y.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Bolch_002c-G_002e-255">Bolch, G.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Bolch_002c-G_002e-136">Bolch, G.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Bolch_002c-G_002e-120">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-114">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-106">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-92">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-85">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-73">Bolch, G.</a>: <a href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a></li>
+<li><a href="#index-Buzen_002c-J_002e-P_002e-189">Buzen, J. P.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Casale_002c-G_002e-369">Casale, G.</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Casale_002c-G_002e-169">Casale, G.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-257">de Meer, H.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-138">de Meer, H.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-de-Meer_002c-H_002e-122">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-116">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-108">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-94">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-87">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-75">de Meer, H.</a>: <a href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a></li>
+<li><a href="#index-Graham_002c-G_002e-S_002e-330">Graham, G. S.</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Graham_002c-G_002e-S_002e-231">Graham, G. S.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Graham_002c-G_002e-S_002e-182">Graham, G. S.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Greiner_002c-S_002e-256">Greiner, S.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Greiner_002c-S_002e-137">Greiner, S.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Greiner_002c-S_002e-121">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-115">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-107">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-93">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-86">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-74">Greiner, S.</a>: <a href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a></li>
+<li><a href="#index-Hsieh_002c-C_002e-H-367">Hsieh, C. H</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Jain_002c-R_002e-148">Jain, R.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Kerola_002c-T_002e-359">Kerola, T.</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Kobayashi_002c-H_002e-201">Kobayashi, H.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Lam_002c-S_002e-368">Lam, S.</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Lavenberg_002c-S_002e-S_002e-254">Lavenberg, S. S.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Lavenberg_002c-S_002e-S_002e-147">Lavenberg, S. S.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Lazowska_002c-E_002e-D_002e-328">Lazowska, E. D.</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Lazowska_002c-E_002e-D_002e-229">Lazowska, E. D.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Lazowska_002c-E_002e-D_002e-180">Lazowska, E. D.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Muntz_002c-R_002e-R_002e-370">Muntz, R. R.</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Reiser_002c-M_002e-253">Reiser, M.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Reiser_002c-M_002e-146">Reiser, M.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Santini_002c-S_002e-237">Santini, S.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Schweitzer_002c-P_002e-273">Schweitzer, P.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Schwetman_002c-H_002e-236">Schwetman, H.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Schwetman_002c-H_002e-199">Schwetman, H.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Serazzi_002c-G_002e-371">Serazzi, G.</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Sevcik_002c-K_002e-C_002e-331">Sevcik, K. C.</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Sevcik_002c-K_002e-C_002e-232">Sevcik, K. C.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Sevcik_002c-K_002e-C_002e-183">Sevcik, K. C.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-258">Trivedi, K.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-139">Trivedi, K.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Trivedi_002c-K_002e-123">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-117">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-109">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-95">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-88">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-76">Trivedi, K.</a>: <a href="#Mean-time-to-absorption-_0028CTMC_0029">Mean time to absorption (CTMC)</a></li>
+<li><a href="#index-Wong_002c-E_002e-242">Wong, E.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Zahorjan_002c-J_002e-329">Zahorjan, J.</a>: <a href="#Bounds-Analysis">Bounds Analysis</a></li>
+<li><a href="#index-Zahorjan_002c-J_002e-230">Zahorjan, J.</a>: <a href="#Multiple-Class-Models">Multiple Class Models</a></li>
+<li><a href="#index-Zahorjan_002c-J_002e-181">Zahorjan, J.</a>: <a href="#Single-Class-Models">Single Class Models</a></li>
+<li><a href="#index-Zeng_002c-G_002e-98">Zeng, G.</a>: <a href="#The-Erlang_002dB-Formula">The Erlang-B Formula</a></li>
+   </ul></body></html>
+
diff --git a/doc/queueing.pdf b/doc/queueing.pdf
new file mode 100644
index 0000000..e1b0143
Binary files /dev/null and b/doc/queueing.pdf differ
diff --git a/doc/queueing.texi b/doc/queueing.texi
new file mode 100644
index 0000000..019d5c0
--- /dev/null
+++ b/doc/queueing.texi
@@ -0,0 +1,221 @@
+% Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Moreno Marzolla
+%
+% This file is part of the queueing package.
+%
+% The queueing package 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 package 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 package; 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
+ at setfilename queueing.info
+
+ at c The following macro is used for the on-line help system, but we don't
+ at c want lots of `See also: foo, bar, and baz' strings cluttering the
+ at c printed manual (that information should be in the supporting text for
+ at c each group of functions and variables).
+
+ at macro seealso {args}
+ at iftex
+ at vskip 2pt
+ at end iftex
+ at ifnottex
+ at sp 1
+ at end ifnottex
+ at noindent
+ at strong{See also:} \args\.
+ at end macro
+
+ at c @macro examplefile{file}
+ at c @example
+ at c @group
+ at c @verbatiminclude @value{top_srcdir}/examples/\file\
+ at c @end group
+ at c @end example
+ at c @end macro
+
+ at c @macro GETHELP{file}
+
+ at c @end macro
+
+ at c @macro GETDEMO{file,n}
+
+ at c @end macro
+
+ at c FIXME: The following macros are workaround to fix erratic behavior of texinfo
+ at iftex
+ at macro lambdack
+ at lambda_{c,k}
+ at end macro
+ at end iftex
+ at ifnottex
+ at macro lambda
+lambda
+ at end macro
+ at macro lambdack
+lambda_@{c, k@}
+ at end macro
+ at end ifnottex
+
+ at ifinfo
+ at format
+START-INFO-DIR-ENTRY
+* queueing: (octave).	Queueing Networks and Markov chains analysis package.
+END-INFO-DIR-ENTRY
+ at end format
+ at end ifinfo
+
+ at c Settings for printing on 8-1/2 by 11 inch paper:
+ at c -----------------------------------------------
+
+ at setchapternewpage odd
+
+ at c Settings for small book format:
+ at c ------------------------------
+
+ at ignore
+ at smallbook
+ at setchapternewpage odd
+ at finalout
+ at iftex
+ at cropmarks
+ at end iftex
+ at end ignore
+
+ at defindex op
+ at defindex au
+
+ at c Things like the Octave version number are defined in conf.texi.
+ at c This file doesn't include a chapter, so it must not be included
+ at c if you want to run the Emacs function texinfo-multiple-files-update.
+
+ at include conf.texi
+
+ at settitle queueing
+ at documentdescription
+User manual for the queueing package, 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 package 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.
+ at end documentdescription
+
+ at ifnottex
+
+Copyright @copyright{} 2008, 2009, 2010, 2011, 2012, 2013, 2014 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.
+
+ at 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).
+
+ at 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.
+ at end ifnottex
+
+ at titlepage
+ at title The Octave Queueing Package
+ at subtitle User's Guide, Edition 1 for release @value{VERSION}
+ at subtitle @value{VERSIONDATE}
+ at author Moreno Marzolla
+ at page
+ at vskip 0pt plus 1filll
+Copyright @copyright{} 2008, 2009, 2010, 2011, 2012, 2013, 2014 Moreno Marzolla (@email{moreno.marzolla@@unibo.it}).
+
+This is the first edition of the Queueing package 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.
+ at end titlepage
+
+ at contents
+
+ at ifnottex
+ at node Top
+ at top
+
+This manual documents how to install and run the Queueing package.
+It corresponds to version @value{VERSION} of the package.
+ at end ifnottex
+
+ at c ------------------------------------------------------------------------
+
+ at menu
+* Summary::
+* Installation and Getting Started:: Installation of the queueing package.
+* Markov Chains::               Functions for Markov Chains.
+* Single Station Queueing Systems:: Functions for single-station queueing systems.
+* Queueing Networks::           Functions for queueing networks.
+* References::                  References
+* 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.
+ at end menu
+
+ at c ------------------------------------------------------------------------
+
+ at include summary.texi
+ at include installation.texi
+ at include markovchains.texi
+ at include singlestation.texi
+ at include queueingnetworks.texi
+ at include references.texi
+
+ at c
+ at c Appendix starts here
+ at c
+ at include gpl.texi
+
+ at c
+ at c INDEX
+ at c
+
+ at node Concept Index
+ at unnumbered Concept Index
+
+ at printindex cp
+
+ at node Function Index
+ at unnumbered Function Index
+
+ at printindex fn
+
+ at node Author Index
+ at unnumbered Author Index
+
+ at printindex au
+
+ at bye
diff --git a/doc/queueingnetworks.texi b/doc/queueingnetworks.texi
new file mode 100644
index 0000000..ebd0bd1
--- /dev/null
+++ b/doc/queueingnetworks.texi
@@ -0,0 +1,4022 @@
+ at c This file has been automatically generated from queueingnetworks.txi
+ at c by proc.m. Do not edit this file, all changes will be lost
+
+ at c -*- texinfo -*-
+
+ at c Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014 Moreno Marzolla
+ at c
+ at c This file is part of the queueing package.
+ at c
+ at c The queueing package is free software; you can redistribute it
+ at c and/or modify it under the terms of the GNU General Public License
+ at c as published by the Free Software Foundation; either version 3 of
+ at c the License, or (at your option) any later version.
+ at c
+ at c The queueing package is distributed in the hope that it will be
+ at c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ at c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ at c GNU General Public License for more details.
+ at c
+ at c You should have received a copy of the GNU General Public License
+ at c along with the queueing package; see the file COPYING.  If not, see
+ at c <http://www.gnu.org/licenses/>.
+
+ at node Queueing Networks
+ at chapter Queueing Networks
+
+ at menu
+* Introduction to QNs::             A brief introduction to Queueing Networks
+* Single Class Models::             Queueing Models with a single job class
+* Multiple Class Models::           Queueing Models with multiple job classess
+* Generic Algorithms::              High-level functions for QN analysis
+* Bounds Analysis::                 Computation of asymptotic performance bounds
+* QN Analysis Examples::            Queueing Networks Analysis examples
+ at end menu
+
+ at cindex queueing networks
+
+ at c
+ at c INTRODUCTION
+ at c
+ at node Introduction to QNs
+ at section Introduction to QNs
+
+Queueing Networks (QN) are a very simple yet powerful modeling tool
+which can be used to analyze many kind of systems. In its simplest
+form, a QN is made of @math{K} service centers. Each center @math{k}
+has a queue, which is connected to @math{m_k} (generally identical)
+ at emph{servers}. Arriving customers (requests) join the queue if there
+is a slot available. Then, requests are served according to a
+(de)queueing policy (e.g., FIFO). After service completes, requests
+leave the server and can join another queue or exit from the system.
+
+Service centers for which @math{m_k = \infty} are called @emph{delay
+centers} or @emph{infinite servers}. In this kind of centers, every
+request always finds one available server, so queueing never occurs.
+
+Requests join the queue according to a @emph{queueing policy}, such as:
+
+ at table @strong
+
+ at item FCFS
+First-Come-First-Served
+
+ at item LCFS-PR
+Last-Come-First-Served, Preemptive Resume
+
+ at item PS
+Processor Sharing
+
+ at item IS
+Infinite Server (for which @math{m_k = \infty}).
+
+ at end table
+
+Queueing models can be @emph{open} or @emph{closed}. In open models
+there is an infinite population of requests; new customers are
+generated outside the system, and eventually leave the system. In
+closed systems there is a fixed population of request.
+
+Queueing models can have a single request class (@emph{single class
+models}), meaning that all requests behave in the same way (e.g., they
+spend the same average time on each particular server). In
+ at emph{multiple class models} there are multiple request classes, each
+one with its own parameters. Furthermore, in multiclass models there
+can be open and closed classes of requests within the same system.
+
+A particular class of QN models, @emph{product-form} QNs, is of
+particular interest. Product-form networks fulfill the following
+assumptions:
+
+ at itemize
+
+ at item The network can consist of open and closed job classes.
+
+ at item The following queueing disciplines are allowed: FCFS, PS, LCFS-PR and IS.
+
+ at 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.
+
+ at 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.
+
+ at 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
+ at math{U} job sources are assigned to the @math{U} chains; the arrival
+rate can be load dependent.
+
+ at end itemize
+
+Product-form networks are attractive because they be efficiently
+analyzed to compute steady-state performance measures.
+
+ at c
+ at c Single Class Models
+ at c
+
+ at node Single Class Models
+ at section 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.
+
+ at noindent @strong{Model Inputs}
+
+ at table @math
+
+ at item @lambda_k
+(Open models only) External arrival rate to service center @math{k}.
+
+ at item @lambda
+(Open models only) Overall external arrival rate to the system as a whole: @math{\lambda =
+\sum_k \lambda_k}.
+
+ at item N
+(Closed models only) Total number of requests in the system.
+
+ at item S_k
+Average service time. @math{S_k} is the average service time on service
+center @math{k}. In other words, @math{S_k} 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).
+
+ at item P_{i, j}
+Routing probability matrix. @math{{\bf P} = [P_{i, j}]} is a @math{K
+\times K} matrix such that @math{P_{i, j}} 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_{i,
+j}}.
+
+ at item V_k
+Mean number of visits the center @math{k} (also called @emph{visit
+ratio} or @emph{relative arrival rate}).
+
+ at end table
+
+ at noindent @strong{Model Outputs}
+
+ at table @math
+
+ at item U_k
+Service center utilization. @math{U_k} is the utilization of service
+center @math{k}. The utilization is defined as the fraction of time in
+which the resource is busy (i.e., the server is processing requests).
+If center @math{k} is a single-server or multiserver node, then
+ at math{0 @leq{} U_k @leq{} 1}. If center @math{k} is an infinite server
+node (delay center), then @math{U_k} denotes the @emph{traffic
+intensity} and is defined as @math{U_k = X_k S_k}; in this case the
+utilization may be greater than one.
+
+ at item R_k
+Average response time. @math{R_k} is the average response time of
+service center @math{k}. The average response time is defined as the
+average time between the arrival of a customer in the queue, and the
+completion of service.
+
+ at item Q_k
+Average number of customers. @math{Q_k} is the average number of
+requests in service center @math{k}. This includes both the requests in
+the queue, and the request being served.
+
+ at item X_k
+Throughput. @math{X_k} is the throughput of service center @math{k}.
+The throughput is defined as the ratio of job completions (i.e., average
+number of jobs completed over a fixed interval of time).
+
+ at end table
+
+ at noindent Given these output parameters, additional performance measures can
+be computed as follows:
+
+ at table @math
+
+ at item X
+System throughput, @math{X = X_k / V_k} for any @math{k} for 
+which @math{V_k \neq 0}
+
+ at item R
+System response time, @math{R = \sum_{k=1}^K R_k V_k}
+
+ at item Q
+Average number of requests in the system, @math{Q = \sum_{k=1} Q_k}; for
+closed systems, this can be written as @math{Q = N-XZ};
+
+ at 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 @math{V_j} satisfy the following equation:
+
+ at iftex
+ at tex
+$$ V_j = P_{0, j} + \sum_{i=1}^K V_i P_{i, j} \quad j=1, \ldots, K $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+                  K
+                 ___
+                \
+V_j = P_(0, j) + >   V_i P_(i, j)    j=1,...,K
+                /___
+                 i=1
+ at end group
+ at end example
+ at end ifnottex
+
+ at 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
+ at math{P_{0, j} = \lambda_j / \lambda}.
+
+For closed models, the visit ratios satisfy the following equation:
+
+ at iftex
+ at tex
+$$\left\{\eqalign{V_j & = \sum_{i=1}^K V_i P_{i, j} \quad j=1, \ldots, K \cr
+                  V_r & = 1 \quad \hbox{for a selected reference station $r$}}\right. $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+/
+|         K
+|        ___
+|       \
+| V_j =  >   V_i P_(i, j)     j=1,...,K
+|       /___
+|        i=1
+|
+| V_r = 1                     for a selected reference station r
+\
+ at end example
+ at end ifnottex
+
+Note that the set of traffic equations @math{V_j = \sum_{i=1}^K V_i
+P_{i, j}} alone can only be solved up to a multiplicative constant; to
+get a unique solution we impose an additional constraint @math{V_r =
+1}. This constraint is equivalent to defining station @math{r} as the
+ at emph{reference station}; the default is @math{r=1},
+ at pxref{doc-qncsvisits}. A job that returns to the reference station is
+assumed to have completed its activity cycle. The network throughput
+is set to the throughput of the reference station.
+
+ at anchor{doc-qncsvisits}
+
+
+ at deftypefn {Function File} {@var{V} =} qncsvisits (@var{P})
+ at deftypefnx {Function File} {@var{V} =} qncsvisits (@var{P}, @var{r})
+
+Compute the mean number of visits to the service centers of a
+single class, closed network with @math{K} service centers.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item P
+ at 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 must be strongly connected, meaning that each node must be
+reachable from every other node.
+
+ at item r
+Index of the reference station, @math{r \in @{1, @dots{}, K@}};
+Default @code{@var{r}=1}. The traffic equations are solved by
+imposing the condition @code{@var{V}(r) = 1}. A request returning to
+the reference station completes its activity cycle.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item V
+ at code{@var{V}(i)} is the average number of visits to service center
+ at math{i}, assuming center @math{r} as the reference station.
+
+ at end table
+
+ at end deftypefn
+
+
+ at anchor{doc-qnosvisits}
+
+
+ at deftypefn {Function File} {@var{V} =} qnosvisits (@var{P}, @var{lambda})
+
+Compute the average number of visits to the service centers of a single 
+class open Queueing Network with @math{K} service centers.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item P
+ at code{@var{P}(i,j)} is the probability that a request which completed
+service at center @math{i} is routed to center @math{j}. 
+
+ at item lambda
+ at code{@var{lambda}(i)} is the external arrival rate to
+center @math{i}. 
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item V
+ at code{@var{V}(i)} is the average number of
+visits to server @math{i}.
+
+ at end table
+
+ at end deftypefn
+
+
+ at c
+ at c
+ at c
+ at noindent @strong{EXAMPLE}
+
+ at float Figure,fig:qn_closed_single
+ at center @image{qn_closed_single,3in}
+ at caption{Closed network with a single class of requests}
+ at end float
+
+ at ref{fig:qn_closed_single} shows a closed QN with a single class of
+requests. The network has three service centers, labeled @emph{CPU},
+ at emph{Disk1}, @emph{Disk2}. and represents the so called @emph{central
+server} model of a computer system. Requests spend some time at the
+CPU, which is represented by a PS (Processor Sharing) node. After
+that, requests are routed to node Disk1 with probability @math{0.3},
+and to node Disk2 with probability @math{0.7}. Both Disk1 and Disk2
+are modeled as FCFS nodes.
+
+If we enumerate the servers as CPU=1, Disk1=2, Disk2=3, we can define
+the routing matrix as follows:
+
+ at iftex
+ at tex
+$$
+P = \pmatrix{ 0 & 0.3 & 0.7 \cr
+              1 & 0 & 0 \cr
+              1 & 0 & 0 }
+$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+    / 0  0.3  0.7 \
+P = | 1  0    0   |
+    \ 1  0    0   /
+ at end example
+ at end ifnottex
+
+The visit ratios @math{V} using station 1 as the reference
+station can be computed with:
+
+ at example
+ at verbatim
+ P = [0 0.3 0.7; ...
+      1 0   0  ; ...
+      1 0   0  ];
+ V = qncsvisits(P)
+ at end verbatim
+   @result{} V = 1.00000   0.30000   0.70000
+ at end example
+
+ at noindent @strong{EXAMPLE}
+
+ at float Figure,fig:qn_open_single
+ at center @image{qn_open_single,3in}
+ at caption{Open Queueing Network with a single class of requests}
+ at end float
+
+ at ref{fig:qn_open_single} shows a open QN with a single class of
+requests. The network has the same structure as the one in
+ at ref{fig:qn_closed_single}, with the difference that here we have a
+stream of jobs arriving from outside the system, at a rate
+ at math{\lambda}. After service completion at the CPU, a job can leave
+the system with probability 0.2, or be transferred to other nodes with
+the probabilities shown in the figure.
+
+The routing matrix of this network is
+
+ at iftex
+ at tex
+$$
+P = \pmatrix{ 0 & 0.3 & 0.5 \cr
+              1 & 0 & 0 \cr
+              1 & 0 & 0 }
+$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+    / 0  0.3  0.5 \
+P = | 1  0    0   |
+    \ 1  0    0   /
+ at end example
+ at end ifnottex
+
+If we let @math{\lambda = 1.2}, we can compute the visit ratios
+ at math{V} as follows:
+
+ at example
+ at verbatim
+ p = 0.3;
+ lambda = 1.2
+ P = [0 0.3 0.5; ...
+      1 0   0  ; ...
+      1 0   0  ];
+ V = qnosvisits(P,[1.2 0 0])
+ at end verbatim
+   @result{} V = 5.0000   1.5000   2.5000
+ at end example
+
+Function @command{qnosvisits} expects a vector with @math{K} elements
+as a second parameter, for open networks only. The vector contains the
+arrival rates at each individual node; since in our example external
+arrivals exist only for node @math{S_1} with rate @math{\lambda =
+1.2}, the second parameter is @code{[1.2, 0, 0]}.
+
+ at c
+ at c Open Networks
+ at c
+ at subsection Open Networks
+
+Jackson networks satisfy the following conditions:
+
+ at itemize
+
+ at item
+There is only one job class in the network; the overall number of jobs
+in the system is unlimited.
+
+ at 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.
+
+ at item
+Arrival rates as well as routing probabilities are independent from
+the number of nodes in the network.
+
+ at item
+External arrivals and service times at the service centers are
+exponentially distributed, and in general can be load-dependent.
+
+ at item
+Service discipline at each node is FCFS
+
+ at 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}:
+
+ at iftex
+ at tex
+$$ \pi(k_1, k_2, \ldots, k_N) = \prod_{i=1}^N \pi_i(k_i) $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at var{joint_prob} = prod( @var{pi} )
+ at end example
+ at end ifnottex
+
+ at noindent where @math{\pi_i(k_i)} is the steady-state probability
+that there are @math{k_i} requests at service center @math{i}.
+
+ at anchor{doc-qnos}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnos (@var{lambda}, @var{S}, @var{V}) 
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnos (@var{lambda}, @var{S}, @var{V}, @var{m})
+
+ at cindex open network, single class
+ at 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:
+
+ at itemize
+
+ at item The allowed service disciplines at network nodes are: FCFS,
+PS, LCFS-PR, IS (infinite server);
+
+ at item Service times are exponentially distributed and
+load-independent; 
+
+ at item Service center @math{k} can consist of @code{@var{m}(k) @geq{} 1} 
+identical servers.
+
+ at item Routing is load-independent
+
+ at end itemize
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Overall external arrival rate (@code{@var{lambda}>0}).
+
+ at item S
+ at code{@var{S}(k)} is the average service time at center
+ at math{i} (@code{@var{S}(k)>0}).
+
+ at item V
+ at code{@var{V}(k)} is the average number of visits to center
+ at math{k} (@code{@var{V}(k) @geq{} 0}).
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at center @math{i}. If
+ at code{@var{m}(k) < 1}, enter @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 all @math{k}.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+If @math{k} is a queueing center, 
+ at 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
+ at emph{traffic intensity} defined as @code{@var{X}(k)*@var{S}(k)}.
+
+ at item R
+ at code{@var{R}(k)} is the average response time of center @math{k}.
+
+ at item Q
+ at code{@var{Q}(k)} is the average number of requests at center
+ at math{k}.
+
+ at item X
+ at code{@var{X}(k)} is the throughput of center @math{k}.
+
+ at end table
+
+ at seealso{qnopen,qnclosed,qnosvisits}
+
+ at end deftypefn
+
+
+From the results computed by this function, it is possible to derive
+other quantities of interest as follows:
+
+ at itemize
+
+ at item
+ at strong{System Response Time}: The overall system response time
+can be computed as
+ at iftex
+ at tex
+$R_s = \sum_{k=1}^K V_i R_k$
+ at end tex
+ at end iftex
+ at ifnottex
+ at code{R_s = dot(V,R);}
+ at end ifnottex
+
+ at item
+ at strong{Average number of requests}: The average number of requests
+in the system can be computed as:
+ at iftex
+ at tex
+$Q_s = \sum_{k=1}^K Q(k)$
+ at end tex
+ at end iftex
+ at ifnottex
+ at code{Q_s = sum(Q)}
+ at end ifnottex
+
+ at end itemize
+
+ at noindent @strong{EXAMPLE}
+
+ at example
+ at verbatim
+ lambda = 3;
+ V = [16 7 8];
+ S = [0.01 0.02 0.03];
+ [U R Q X] = qnos( lambda, S, V );
+ R_s = dot(R,V) # System response time
+ N = sum(Q) # Average number in system
+ at end verbatim
+ at print{} R_s =  1.4062
+ at print{} N =  4.2186
+ at end example
+
+ at noindent @strong{REFERENCES}
+
+ at 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.
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at c
+ at c Closed Networks
+ at c
+ at subsection Closed Networks
+
+ at anchor{doc-qncsmva}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qncsmva (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qncsmva (@var{N}, @var{S}, @var{V}, @var{m})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qncsmva (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+
+ at cindex Mean Value Analysys (MVA)
+ at cindex closed network, single class
+ at 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{qncsmvald} instead.
+
+Additionally, the normalization constant @math{G(n)}, @math{n=0,
+ at dots{}, N} is computed; @math{G(n)} can be used in conjunction with
+the BCMP theorem to compute steady-state probabilities.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+Population size (number of requests in the system, @code{@var{N} @geq{} 0}).
+If @code{@var{N} == 0}, this function returns
+ at code{@var{U} = @var{R} = @var{Q} = @var{X} = 0}
+
+ at item S
+ at code{@var{S}(k)} is the mean service time at center @math{k}
+(@code{@var{S}(k) @geq{} 0}).
+
+ at item V
+ at code{@var{V}(k)} is the average number of visits to service center
+ at math{k} (@code{@var{V}(k) @geq{} 0}).
+
+ at item Z
+External delay for customers (@code{@var{Z} @geq{} 0}). Default is 0.
+
+ at item m
+ at 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
+ at 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
+ at code{@var{m}(k)} servers. Default is @code{@var{m}(k) = 1} for all
+ at math{k} (each service center has a single server).
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+If @math{k} is a FCFS, LCFS-PR or PS node (@code{@var{m}(k) @geq{}
+1}), then @code{@var{U}(k)} is the utilization of center @math{k},
+ at math{0 @leq{} U(k) @leq{} 1}. 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)}. In this case the
+value of @code{@var{U}(k)} may be greater than one.
+
+ at item R
+ at code{@var{R}(k)} is the response time at center @math{k}.
+The @emph{Residence Time} at center @math{k} is
+ at code{@var{R}(k) * @var{V}(k)}.
+The system response time @var{Rsys}
+can be computed either as @code{@var{Rsys} = @var{N}/@var{Xsys} - Z}
+or as @code{@var{Rsys} = dot(@var{R}, at var{V})}
+
+ at item Q
+ at code{@var{Q}(k)} is the average number of requests at center
+ at math{k}. The number of requests in the system can be computed
+either as @code{sum(@var{Q})}, or using the formula
+ at code{@var{N}- at var{Xsys}*@var{Z}}.
+
+ at item X
+ at code{@var{X}(k)} is the throughput of center @math{k}. The
+system throughput @var{Xsys} can be computed as
+ at code{@var{Xsys} = @var{X}(1) / @var{V}(1)}
+
+ at 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.
+
+ at end table
+
+ at quotation Note on numerical stability
+In presence of load-dependent servers (e.g., if @code{@var{m}(k)>1}
+for some @math{k}), the MVA algorithm is known to be numerically
+unstable. Generally this problem manifests itself as negative
+response times or utilizations. This is not a problem with the
+ at code{queueing} toolbox, but with the Mean Value Analysis algorithm,
+and therefore has currently no easy workaround (aoart from using a
+different solution technique, if available). This function prints a
+warning if it detects numerical problems; you can disable the warning
+with the command @code{warning("off", "qn:numerical-instability")}.
+ at end quotation
+
+ at seealso{qncsmvald,qncscmva}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent 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. @uref{http://doi.acm.org/10.1145/322186.322195, 10.1145/322186.322195}
+
+ at auindex Reiser, M.
+ at 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 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".
+
+ at auindex Jain, R.
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at noindent @strong{EXAMPLE}
+
+ at example
+ at verbatim
+ S = [ 0.125 0.3 0.2 ];
+ V = [ 16 10 5 ];
+ N = 20;
+ m = ones(1,3);
+ Z = 4;
+ [U R Q X] = qncsmva(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 );
+ at end verbatim
+ at end example
+
+ at c
+ at c MVA for single class, closed networks with load dependent servers
+ at c
+ at anchor{doc-qncsmvald}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvald (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvald (@var{N}, @var{S}, @var{V}, @var{Z})
+
+ at cindex Mean Value Analysys (MVA)
+ at cindex MVA
+ at cindex closed network, single class
+ at 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
+ at code{qncsmva} is more efficient.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at 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}
+
+ at item S
+ at code{@var{S}(k,n)} is the mean service time at center @math{k}
+where there are @math{n} requests, @math{1 @leq{} n
+ at 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.
+
+ at item V
+ at code{@var{V}(k)} is the average number
+of visits to service center @math{k} (@code{@var{V}(k) @geq{} 0}).
+
+ at item Z
+external delay ("think time", @code{@var{Z} @geq{} 0}); default 0.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+ at code{@var{U}(k)} is the utilization of service center @math{k}. The
+utilization is defined as the probability that service center
+ at math{k} is not empty, that is, @math{U_k = 1-\pi_k(0)} where
+ at math{\pi_k(0)} is the steady-state probability that there are 0
+jobs at service center @math{k}.
+
+ at item R
+ at code{@var{R}(k)} is the response time on service center @math{k}.
+
+ at item Q
+ at code{@var{Q}(k)} is the average number of requests in service center
+ at math{k}.
+
+ at item X
+ at code{@var{X}(k)} is the throughput of service center @math{k}.
+
+ at end table
+
+ at quotation Note
+In presence of load-dependent servers,
+the MVA algorithm is known to be numerically
+unstable. Generally this problem manifests itself as negative
+response times produced by this function.
+ at end quotation
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent 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. @uref{http://doi.acm.org/10.1145/322186.322195, 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''.
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at c
+ at c CMVA for single class, closed networks with a single load dependent servers
+ at c
+ at anchor{doc-qncscmva}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncscmva (@var{N}, @var{S}, @var{Sld}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncscmva (@var{N}, @var{S}, @var{Sld}, @var{V}, @var{Z})
+
+ at cindex conditional MVA (CMVA)
+ at cindex Mean Value Analysis, conditional (CMVA)
+ at cindex closed network, single class
+ at cindex CMVA
+
+This is the implementation of the original Conditional MVA (CMVA)
+algorithm, a numerically stable variant of MVA, as described in G.
+Casale, @cite{A Note on Stable Flow-Equivalent Aggregation in Closed
+Networks}. This function supports a network of @math{M @geq{} 1}
+service centers and a single delay center. Servers @math{1, @dots{},
+M-1} are load-independent; server @math{M} is load-dependent.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+Number of requests in the system, @code{@var{N} @geq{} 0}. If
+ at code{@var{N} == 0}, this function returns @code{@var{U} = @var{R} =
+ at var{Q} = @var{X} = 0}
+
+ at item S
+Vector of mean service times for load-independent (fixed rate) servers.
+Specifically, @code{@var{S}(k)} is the mean service time on server
+ at math{k = 1, @dots{}, M-1} (@code{@var{S}(k) > 0}). If there are no
+fixed-rate servers, then @code{S = []}
+
+ at item Sld
+ at code{@var{Sld}(n)} is the inverse service rate at server @math{M}
+(the load-dependent server) when there are @math{n} requests,
+ at math{n=1, @dots{}, N}. @code{@var{Sld}(n) = } @math{1 / \mu(n)}.
+
+ at item V
+ at code{@var{V}(k)} is the average number of visits to service center
+ at math{k=1, @dots{}, M}, where @code{@var{V}(k) @geq{} 0}.
+ at code{@var{V}(1:M-1)} are the visit rates to the fixed rate servers;
+ at code{@var{V}(M)} is the visit rate to the load dependent server.
+
+ at item Z
+External delay for customers (@code{@var{Z} @geq{} 0}). Default is 0.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+ at code{@var{U}(k)} is the utilization of center @math{k} (@math{k=1, @dots{}, M})
+
+ at item R
+ at code{@var{R}(k)} is the response time of center @math{k}, (@math{k=1,
+ at dots{}, M}). The system response time @var{Rsys} can be computed as
+ at code{@var{Rsys} = @var{N}/@var{Xsys} - Z}
+
+ at item Q
+ at code{@var{Q}(k)} is the average number of requests at center @math{k}, (@math{k=1, @dots{}, M}).
+
+ at item X
+ at code{@var{X}(k)} is the throughput of center @math{k}, (@math{k=1, @dots{}, M}).
+
+ at end table
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent G. Casale. @cite{A note on stable flow-equivalent aggregation in
+closed networks}. Queueing Syst. Theory Appl., 60:193–-202, December
+2008, @uref{http://dx.doi.org/10.1007/s11134-008-9093-6, 10.1007/s11134-008-9093-6}
+
+ at auindex Casale, G.
+
+ at c
+ at c Approximate MVA for single class, closed networks
+ at c
+
+ at anchor{doc-qncsmvaap}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvaap (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvaap (@var{N}, @var{S}, @var{V}, @var{m})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol}, @var{iter_max})
+
+ at cindex Mean Value Analysys (MVA), approximate
+ at cindex MVA, approximate
+ at cindex approximate MVA
+ at cindex closed network, single class
+ at 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
+ at code{qncsmvald} instead.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+Population size (number of requests in the system, @code{@var{N} > 0}).
+
+ at item S
+ at code{@var{S}(k)} is the mean service time on server @math{k}
+(@code{@var{S}(k)>0}).
+
+ at item V
+ at code{@var{V}(k)} is the average number of visits to service center
+ at math{k} (@code{@var{V}(k) @geq{} 0}).
+
+ at item m
+ at 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
+ at code{@var{m}(k) < 1}, center @math{k} is a delay center (IS); if
+ at 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}).
+
+ at item Z
+External delay for customers (@code{@var{Z} @geq{} 0}). Default is 0.
+
+ at 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}}.
+
+ at 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.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at 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
+ at math{k} is an IS node (@code{@var{m}(k) < 1}), then
+ at code{@var{U}(k)} is the @emph{traffic intensity} defined as
+ at code{@var{X}(k)*@var{S}(k)}.
+
+ at item R
+ at 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}
+
+ at item Q
+ at code{@var{Q}(k)} is the average number of requests at center
+ at math{k}. The number of requests in the system can be computed
+either as @code{sum(@var{Q})}, or using the formula
+ at code{@var{N}- at var{Xsys}*@var{Z}}.
+
+ at item X
+ at code{@var{X}(k)} is the throughput of center @math{k}. The
+system throughput @var{Xsys} can be computed as
+ at code{@var{Xsys} = @var{X}(1) / @var{V}(1)}
+
+ at end table
+
+ at seealso{qncsmva,qncsmvald}
+
+ at end deftypefn
+
+
+ at 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").
+
+ at auindex Lazowska, E. D.
+ at auindex Zahorjan, J.
+ at auindex Graham, G. S.
+ at auindex Sevcik, K. C.
+
+ at c
+ at c Convolution
+ at c
+
+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:
+
+ at iftex
+ at tex
+$$ \pi(k_1, k_2, \ldots, k_K) = {1 \over G(N)} \prod_{i=1}^N F_i(k_i) $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+ at group
+k = [k1, k2, @dots{} kn]; @r{population vector}
+p = 1/G(N+1) \prod F(i,k);
+ at end group
+ at end example
+ at 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
+ at math{{\bf G} = \left(G(0), G(1), \ldots, G(N)\right)} 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), @dots{} @var{G}(N+1)]} where
+ at 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).
+
+ at command{queueing} implements the convolution algorithm, in the function
+ at command{qncsconv} and @command{qncsconvld}. The first one
+supports single-station nodes, multiple-station nodes and IS nodes.
+The second one supports networks with general load-dependent service
+centers.
+
+
+ at anchor{doc-qncsconv}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qncsconv (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qncsconv (@var{N}, @var{S}, @var{V}, @var{m})
+
+ at cindex closed network, single class
+ at cindex normalization constant
+ at cindex convolution algorithm
+
+Analyze product-form, single class closed networks using the convolution algorithm.
+
+Load-independent service centers, multiple servers (@math{M/M/m}
+queues) and IS nodes are supported. For general load-dependent
+service centers, use @code{qncsconvld} instead.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+Number of requests in the system (@code{@var{N}>0}).
+
+ at item S
+ at code{@var{S}(k)} is the average service time on center @math{k}
+(@code{@var{S}(k) @geq{} 0}).
+
+ at item V
+ at code{@var{V}(k)} is the visit count of service center @math{k}
+(@code{@var{V}(k) @geq{} 0}).
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at center
+ at 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}.
+
+ at end table
+
+ at strong{OUTPUT}
+
+ at table @var
+
+ at item U
+ at code{@var{U}(k)} is the utilization of center @math{k}. 
+For IS nodes, @code{@var{U}(k)} is the @emph{traffic intensity}.
+
+ at item R
+ at code{@var{R}(k)} is the average response time of center @math{k}.
+
+ at item Q
+ at code{@var{Q}(k)} is the average number of customers at center
+ at math{k}.
+
+ at item X
+ at code{@var{X}(k)} is the throughput of center @math{k}.
+
+ at item G
+Vector of normalization constants. @code{@var{G}(n+1)} contains the value of
+the normalization constant with @math{n} requests
+ at math{G(n)}, @math{n=0, @dots{}, N}.
+
+ at end table
+
+ at seealso{qncsconvld}
+
+ at end deftypefn
+
+
+ at 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)}.
+
+ at 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. @uref{http://doi.acm.org/10.1145/362342.362345, 10.1145/362342.362345}
+
+ at 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.
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at 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:
+
+ at iftex
+ at 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
+$$
+ at end tex
+ at end iftex
+
+ at example
+ at 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] = qncsconv( 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
+ at end verbatim
+ at print{} k(1)=1 prob=0.17975
+ at print{} k(2)=2 prob=0.48404
+ at print{} k(3)=0 prob=0.52779
+ at end example
+
+
+ at c
+ at anchor{doc-qncsconvld}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qncsconvld (@var{N}, @var{S}, @var{V})
+
+ at cindex closed network
+ at cindex normalization constant
+ at cindex convolution algorithm
+ at 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
+ at code{@var{G}=[@var{G}(1), @dots{}, @var{G}(N+1)]} where
+ at code{@var{G}(i+1)} is the value of @math{G(i)}.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+Number of requests in the system (@code{@var{N}>0}).
+
+ at item S
+ at code{@var{S}(k,n)} is the mean service time at center @math{k}
+where there are @math{n} requests, @math{1 @leq{} n
+ at 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.
+
+ at item V
+ at 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.
+
+ at end table
+
+ at strong{OUTPUT}
+
+ at table @var
+
+ at item U
+ at code{@var{U}(k)} is the utilization of center @math{k}.
+
+ at item R
+ at code{@var{R}(k)} is the average response time at center @math{k}.
+
+ at item Q
+ at code{@var{Q}(k)} is the average number of customers in center @math{k}.
+
+ at item X
+ at code{@var{X}(k)} is the throughput of center @math{k}.
+
+ at item G
+Normalization constants (vector). @code{@var{G}(n+1)}
+corresponds to @math{G(n)}, as array indexes in Octave start
+from 1.
+
+ at end table
+
+ at seealso{qncsconv}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent Herb Schwetman, @cite{Some Computational Aspects of Queueing Network
+Models}, Technical Report
+ at uref{http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-354.pdf,
+CSD-TR-354}, Department of Computer Sciences, Purdue University, feb,
+1981 (revised).
+
+ at auindex Schwetman, H.
+
+ at noindent 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. @uref{http://doi.acm.org/10.1145/800200.806187, 10.1145/800200.806187}
+
+ at auindex Reiser, M.
+ at 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 @command{qncsconvld} 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
+ at code{F()} in @command{qncsconvld} which has been made similar to
+function @math{f_i} defined in Schwetman, @cite{Some Computational
+Aspects of Queueing Network Models}.
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at c
+ at c
+ at c
+ at subsection Non Product-Form QNs
+ at anchor{Non Product-Form QNs}
+ at c
+ at c MVABLO algorithm for approximate analysis of closed, single class
+ at c QN with blocking
+ at c
+ at anchor{doc-qncsmvablo}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvablo (@var{N}, @var{S}, @var{M}, @var{P} )
+
+ at cindex queueing network with blocking
+ at cindex blocking queueing network
+ at cindex closed network, finite capacity
+ at cindex MVABLO
+
+Approximate MVA algorithm for closed queueing networks with blocking.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at 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:
+ at code{0 < @var{N} < sum(@var{M})}.
+
+ at item S
+Average service time. @code{@var{S}(k)} is the average service time 
+requested on server @math{k} (@code{@var{S}(k) > 0}).
+
+ at item M
+ at code{@var{M}(k)} is the capacity of center
+ at math{k}. The capacity is the maximum number of requests in a service
+center, including the request currently in service (@code{@var{M}(k) @geq{} 1}).
+
+ at item P
+ at 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}.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+ at code{@var{U}(k)} is the utilization of
+service center @math{i}.
+
+ at item R
+ at code{@var{R}(k)} is the average response time
+of service center @math{i}.
+
+ at item Q
+ at code{@var{Q}(k)} is
+the average number of requests in service center @math{i} (including
+the request in service).
+
+ at item X
+ at code{@var{X}(i)} is the throughput of
+service center @math{i}.
+
+ at end table
+
+ at seealso{qnopen, qnclosed}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent 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.  @uref{http://dx.doi.org/10.1109/32.4663, 10.1109/32.4663}
+
+ at auindex Akyildiz, I. F.
+
+ at anchor{doc-qnmarkov}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmarkov (@var{lambda}, @var{S}, @var{C}, @var{P})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmarkov (@var{lambda}, @var{S}, @var{C}, @var{P}, @var{m})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmarkov (@var{N}, @var{S}, @var{C}, @var{P})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmarkov (@var{N}, @var{S}, @var{C}, @var{P}, @var{m})
+
+ at cindex closed network, multiple classes
+ at cindex closed network, finite capacity
+ at cindex blocking queueing network
+ at 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:
+
+ at itemize @bullet
+
+ at item There exists only a single class of customers.
+
+ at item The network has @math{K} service centers. Center
+ at math{k} has @math{m_k > 0} servers, and has a total (finite) capacity of
+ at math{C_k \geq m_k} which includes both buffer space and servers.
+The buffer space at service center @math{k} is therefore
+ at math{C_k - m_k}.
+
+ at item The network can be open, with external arrival rate to
+center @math{k} equal to 
+ at math{\lambda_k}, or closed with fixed
+population size @math{N}. For closed networks, the population size
+ at math{N} must be strictly less than the network capacity: @math{N < \sum_i C_i}.
+
+ at item Average service times are load-independent.
+
+ at item @math{P_{i, j}} 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_{i, j}}.
+
+ at item Blocking type is Repetitive-Service (RS). Service
+center @math{j} is @emph{saturated} if the number of requests is equal
+to its capacity @math{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.
+
+ at end itemize
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+ at itemx N
+If the first argument is a vector @var{lambda}, it is considered to be
+the external arrival rate @code{@var{lambda}(k) @geq{} 0} to service center
+ at math{k} 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
+ at var{N} must be strictly
+less than the network capacity: @code{@var{N} < sum(@var{C})}.
+
+ at item S
+ at code{@var{S}(k)} is the average service time at service center
+ at math{k}
+
+ at item C
+ at code{@var{C}(k)} is the Capacity of service center @math{k}. The capacity includes both
+the buffer and server space @code{@var{m}(i)}. Thus the buffer space is
+ at code{@var{C}(k)- at var{m}(k)}.
+
+ at item P
+ at code{@var{P}(i,j)} is the transition probability from service center
+ at math{i} to service center @math{j}.
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at service center
+ at math{k}. Note that @code{@var{m}(k) @geq{} @var{C}(k)} for each @var{k}.
+If @var{m} is omitted, all service centers are assumed to have a
+single server (@code{@var{m}(k) = 1} for all @math{k}).
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+ at code{@var{U}(k)} is the utilization of service center @math{k}..
+
+ at item R
+ at code{@var{R}(k)} is the response time on service center @math{k}.
+
+ at item Q
+ at code{@var{Q}(k)} is the average number of customers in the
+service center @math{k}, @emph{including} the request in service.
+
+ at item X
+ at code{@var{X}(k)} is the throughput of service center @math{k}.
+
+ at end table
+
+ at quotation Note
+
+The space complexity of this implementation is
+ at math{O( \prod_{k=1}^K (C_k + 1)^2)}. The time complexity is dominated
+by the time needed to solve a linear system with 
+ at math{\prod_{k=1}^K (C_k + 1)}
+unknowns.
+
+ at end quotation
+
+ at end deftypefn
+
+
+ at c
+ at c
+ at c
+ at node Multiple Class Models
+ at section 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_{c, k}} in service at service center
+ at math{k}. For open models, we denote with @math{{\bf \lambda} =
+\lambda_{c, k}} the arrival rates, where @math{\lambda_{c, k}} is the
+external arrival rate of class @math{c} customers at service center
+ at 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
+ at math{C \times K \times C \times K} matrix @math{{\bf P} = [P_{r, i, s, j}]}
+such that @math{P_{r, i, s, j}} is the probability that a class
+ at 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.
+
+ at noindent @strong{Model Inputs}
+
+ at table @math
+
+ at item @lambdack
+(open networks) External arrival rate of class- at math{c} requests to service center @math{k}
+
+ at item @lambda
+(open networks) Overall external arrival rate to the whole system: @math{\lambda = \sum_c \sum_k \lambda_{c, k}}
+
+ at item N_c
+(closed networks) Number of class @math{c} requests in the system.
+
+ at item S_{c, k}
+Average service time. @math{S_{c, k}} is the average service time on
+service center @math{k} for class @math{c} requests.
+
+ at item P_{r, i, s, j}
+Routing probability matrix. @math{{\bf P} = [P_{r, i, s, j}]} is a @math{C
+\times K \times C \times K} matrix such that @math{P_{r, i, s, j}} 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.
+
+ at item V_{c, k}
+Mean number of visits of class @math{c} requests to center @math{k}.
+
+ at end table
+
+ at noindent @strong{Model Outputs}
+
+ at table @math
+
+ at item U_{c, k}
+Utilization of service center @math{k} 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).  If center @math{k}
+is a single-server or multiserver node, then 
+ at math{0 \leq U_{c, k} \leq 1}. 
+If center @math{k} is an infinite server node (delay
+center), then @math{U_{c, k}} denotes the @emph{traffic intensity} and
+is defined as @math{U_{c, k} = X_{c, k} S_{c, k}}; in this case the
+utilization may be greater than one.
+
+ at item R_{c, k}
+Average response time experienced by class @math{c} requests on service
+center @math{k}. The average response time is defined as the average
+time between the arrival of a customer in the queue, and the completion
+of service.
+
+ at item Q_{c, k}
+Average number of class @math{c} requests on service center
+ at math{k}. This includes both the requests in the queue, and the request
+being served.
+
+ at item X_{c, k}
+Throughput of service center @math{k} for class @math{c} requests.  The
+throughput is defined as the rate of completion of class @math{c}
+requests.
+
+ at end table
+
+ at noindent It is possible to define aggregate performance measures as follows:
+
+ at table @math
+
+ at item U_k
+Utilization of service center @math{k}:
+ at iftex
+ at tex
+$U_k = \sum_{c=1}^C U_{c, k}$
+ at end tex
+ at end iftex
+ at ifnottex
+ at code{Uk = sum(U,k);}
+ at end ifnottex
+
+ at item R_c
+System response time for class @math{c} requests:
+ at iftex
+ at tex
+$R_c = \sum_{k=1}^K R_{c, k} V_{c, k}$
+ at end tex
+ at end iftex
+ at ifnottex
+ at code{Rc = sum( V.*R, 1 );}
+ at end ifnottex
+
+ at item Q_c
+Average number of class @math{c} requests in the system:
+ at iftex
+ at tex
+$Q_c = \sum_{k=1}^K Q_{c, k}$
+ at end tex
+ at end iftex
+ at ifnottex
+ at code{Qc = sum( Q, 2 );}
+ at end ifnottex
+
+ at item X_c
+Class @math{c} throughput:
+ at iftex
+ at tex
+$X_c = X_{c, k} / V_{c, k}$ for any @math{k} for which @math{V_{c,k} \neq 0}
+ at end tex
+ at end iftex
+ at ifnottex
+ at code{X(c) = X(c,k) ./ V(c,k);} for any @math{k} for which @code{V(c,k) != 0}
+ at end ifnottex
+
+ at end table
+
+For closed networks, we can define the visit ratios @math{V_{s, j}}
+for class @math{s} customers at service center @math{j} as follows:
+
+ at iftex
+ at tex
+$$\left\{\eqalign{ V_{s, j} & = \sum_{r=1}^C \sum_{i=1}^K V_{r, i} P_{r, i, s, j}, \quad s=1, \ldots, C, j=1, \ldots, K \cr
+                   V_{s, r_s} & = 1 \quad s=1, \ldots, C}\right. $$
+ at end tex
+ at end iftex
+ at ifnottex
+ at group
+V_sj = sum_r sum_i V_ri P_risj    s=1,...,C, j=1,...,K
+V_s r_s = 1                       s=1,...,C
+ at end group
+ at end ifnottex
+
+ at noindent where @math{r_s} is the class @math{s}
+reference station. Similarly to single class models, the traffic
+equation for closed multiclass networks can be solved up to
+multiplicative constants unless we choose one reference station for
+each closed chain class and set its visit ratio to 1.
+
+For open networks the traffic equations are as follows:
+
+ at iftex
+ at tex
+$$V_{s, j} = P_{0, s, j} + \sum_{r=1}^C \sum_{i=1}^K V_{r, i} P_{r, i, s, j} \quad s=1, \ldots, C, j=1, \ldots, K$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at group
+V_sj = P_0sj + sum_r sum_i V_ri P_risj  s=1,...,C, j=1,...,K
+ at end group
+ at end ifnottex
+
+ at noindent where @math{P_{0, s, j}} is the probability that an external
+arrival goes to service center @math{j} as a class- at math{s} request.
+If @math{\lambda_{s, j}} is the external arrival rate of class
+ at math{s} requests to service center @math{j}, and @math{\lambda =
+\sum_s \sum_j \lambda_{s, j}} is the overall external arrival rate,
+then @math{P_{0, s, j} = \lambda_{s, j} / \lambda}.
+
+ at anchor{doc-qncmvisits}
+
+
+ at deftypefn {Function File} {[@var{V} @var{ch}] =} qncmvisits (@var{P})
+ at deftypefnx {Function File} {[@var{V} @var{ch}] =} qncmvisits (@var{P}, @var{r})
+
+Compute the average number of visits to the service centers of a closed multiclass network with @math{K} service centers and @math{C} customer classes.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item P
+ at 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 allowed.
+
+ at item r
+ at code{@var{r}(c)} is the index of class @math{c} reference station,
+ at math{r(c) \in @{1, @dots{}, K@}}, @math{c \in @{1, @dots{}, C@}}.
+The class @math{c} visit count to server @code{@var{r}(c)}
+(@code{@var{V}(c,r(c))}) is conventionally set to 1. The reference
+station serves two purposes: (i) its throughput is assumed to be the
+system throughput, and (ii) a job returning to the reference station
+is assumed to have completed one cycle. Default is to consider
+station 1 as the reference station for all classes.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item V
+ at code{@var{V}(c,i)} is the number of visits of class @math{c}
+requests at center @math{i}.
+
+ at item ch
+ at code{@var{ch}(c)} is the chain number that class @math{c} belongs
+to. Different classes can belong to the same chain. Chains are
+numbered sequentially starting from 1 (@math{1, 2, @dots{}}). The
+total number of chains is @code{max(@var{ch})}.
+
+ at end table
+
+ at end deftypefn
+
+
+ at anchor{doc-qnomvisits}
+
+
+ at deftypefn {Function File} {@var{V} =} qnomvisits (@var{P}, @var{lambda})
+
+Compute the visit ratios to the service centers of an open multiclass network with @math{K} service centers and @math{C} customer classes.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item P
+ at 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.
+
+ at item lambda
+ at code{@var{lambda}(r,i)} is the external arrival rate of class @math{r}
+requests to center @math{i}.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item V
+ at code{@var{V}(r,i)} is the visit ratio of class @math{r}
+requests at center @math{i}.
+
+ at end table
+
+ at end deftypefn
+
+
+ at c
+ at c Open Networks
+ at c
+ at subsection Open Networks
+
+ at c
+ at c Open network with multiple classes
+ at c
+ at anchor{doc-qnom}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnom (@var{lambda}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnom (@var{lambda}, @var{S}, @var{V}, @var{m})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnom (@var{lambda}, @var{S}, @var{P})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnom (@var{lambda}, @var{S}, @var{P}, @var{m})
+
+ at cindex open network, multiple classes
+ at cindex multiclass network, open
+
+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
+ at math{K} service centers and @math{C} customer classes.
+
+ at quotation Note
+If this function is called specifying the visit ratios
+ at var{V}, class switching is @strong{not} allowed.
+If this function is called specifying the routing probability matrix
+ at var{P}, then class switching @strong{is} allowed; however, in this
+case all nodes are restricted to be fixed rate servers or delay
+centers: multiple-server and general load-dependent centers are not
+supported.
+Note that the meaning of parameter @var{lambda} is different 
+from one case to the other (see below).
+ at end quotation
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+If this function is invoked as @code{qnom(lambda, S, V, @dots{})},
+then @code{@var{lambda}(c)} is the external arrival rate of class
+ at math{c} customers (@code{@var{lambda}(c) @geq{} 0}). If this
+function is invoked as @code{qnom(lambda, S, P, @dots{})}, then
+ at code{@var{lambda}(c,k)} is the external arrival rate of class
+ at math{c} customers at center @math{k} (@code{@var{lambda}(c,k) @geq{}
+0}).
+
+ at item S
+ at 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, mean service times must be class-independent.
+
+ at item V
+ at code{@var{V}(c,k)} is the visit ratio of class @math{c}
+customers to service center @math{k} (@code{@var{V}(c,k) @geq{} 0 }).
+ at strong{If you pass this argument, class switching is not
+allowed}
+
+ at item P
+ at 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 argument @var{P},
+class switching is allowed}; however, all servers must be fixed-rate or infinite-server nodes (@code{@var{m}(k) @leq{} 1} for all @math{k}).
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at center @math{i}. If
+ at code{@var{m}(k) < 1}, enter @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 all @math{k}.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at 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)}.
+
+ at item R
+ at code{@var{R}(c,k)} is the class @math{c} response time at center
+ at math{k}. The system response time for class @math{c} requests can be
+computed as @code{dot(@var{R}, @var{V}, 2)}.
+
+ at item Q
+ at 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)}
+
+ at item X
+ at code{@var{X}(c,k)} is the class @math{c} throughput
+at center @math{k}.
+
+ at end table
+
+ at seealso{qnopen,qnos,qnomvisits}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at 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 7.4.1 ("Open Model Solution Techniques").
+
+ at auindex Lazowska, E. D.
+ at auindex Zahorjan, J.
+ at auindex Graham, G. S.
+ at auindex Sevcik, K. C.
+
+
+ at subsection Closed Networks
+
+ at c
+ at anchor{doc-qncmpopmix}
+
+
+ at deftypefn {Function File} {pop_mix =} qncmpopmix (@var{k}, @var{N})
+
+ at cindex population mix
+ at 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
+ at code{@var{N}(c)} requests of class @math{c}, a
+ at math{k}-mix @var{mix} is a @math{C}-dimensional vector with the
+following properties:
+
+ at example
+ at group
+all( mix >= 0 );
+all( mix <= N );
+sum( mix ) == k;
+ at end group
+ at end example
+
+ at noindent This function enumerates all valid @math{k}-mixes, such that
+ at code{@var{pop_mix}(i)} is a @math{C} dimensional row vector representing
+a valid population mix, for all @math{i}.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item k
+Total population size of the requested mix. @var{k} must be a nonnegative integer
+
+ at item N
+ at code{@var{N}(c)} is the number of class @math{c} requests.
+The condition @code{@var{k} @leq{} sum(@var{N})} must hold.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item pop_mix
+ at code{@var{pop_mix}(i,c)} is the number of class @math{c} requests
+in the @math{i}-th population mix. The number of
+population mixes is @code{rows( @var{pop_mix} ) }.
+
+ at 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
+ at code{qnmvapop}.
+
+ at seealso{qncmnpop}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent Herb Schwetman, @cite{Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models}, Technical Report
+ at uref{http://www.cs.purdue.edu/research/technical_reports/1980/TR
+80-355.pdf, CSD-TR-355},
+Department of Computer Sciences, Purdue University, feb 15, 1982,
+
+Note that the slightly different problem of generating all tuples
+ at math{k_1, k_2, \ldots, k_N} such that @math{\sum_i k_i = k} and
+ at math{k_i} are nonnegative integers, for some fixed integer @math{k
+ at geq{} 0} has been described in S. Santini, @cite{Computing the
+Indices for a Complex Summation}, unpublished report, available at
+ at url{http://arantxa.ii.uam.es/~ssantini/writing/notes/s668_summation.pdf}
+
+ at auindex Schwetman, H.
+ at auindex Santini, S.
+
+ at c
+ at anchor{doc-qncmnpop}
+
+
+ at deftypefn {Function File} {@var{H} =} qncmnpop (@var{N})
+
+ at cindex population mix
+ at 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.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+Population vector. @code{@var{N}(c)} is the number of class- at math{c}
+requests in the system. The total number of requests in the network
+is @code{sum(@var{N})}.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item H
+ at 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.
+
+ at end table
+
+ at seealso{qncmmva,qncmpopmix}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent 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
+ at uref{http://doi.acm.org/10.1145/1010629.805477, 10.1145/1010629.805477}
+
+ at auindex Zahorjan, J.
+ at auindex Wong, E.
+
+ at c
+ at c MVA for multiple class, closed networks
+ at c
+ at anchor{doc-qncmmva}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S} )
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{V}, @var{m})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{P})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{P}, @var{r})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{P}, @var{r}, @var{m})
+
+ at cindex Mean Value Analysys (MVA)
+ at cindex closed network, multiple classes
+ at cindex multiclass network, closed
+
+Compute steady-state performance measures for closed, multiclass
+queueing networks using the Mean Value Analysys (MVA) algorithm.
+
+Queueing policies at service centers can be any of the following:
+
+ at table @strong
+
+ at 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.
+
+ at item PS
+(Processor Sharing) customers are served in parallel by a single
+server, each customer receiving an equal share of the service rate.
+
+ at 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.
+
+ at item IS
+(Infinite Server) customers are delayed independently of other
+customers at the service center (there is effectively an infinite
+number of servers).
+
+ at end table
+
+ at quotation Note
+If this function is called specifying the visit ratios
+ at var{V}, class switching is @strong{not} allowed.
+
+If this function is called specifying the routing probability matrix
+ at var{P}, then class switching @strong{is} allowed; however, in this
+case all nodes are restricted to be fixed rate servers or delay
+centers: multiple-server and general load-dependent centers are not
+supported.
+ at end quotation
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+ at 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 for all @var{k},
+ at code{@var{U}(c,k) = @var{R}(c,k) = @var{Q}(c,k) = @var{X}(c,k) = 0}
+
+ at item S
+ at 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 the
+service time at center @math{k} is class-dependent, i.e., different
+classes have different service times at center @math{k}, then center
+ at 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, i.e., all classes
+ at strong{must} have the same service time.
+
+ at item V
+ at 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.
+ at strong{If you pass this argument, class switching is not
+allowed}
+
+ at item P
+ at 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; the reference stations for each class
+are specified with the paramter @var{r}.
+ at strong{If you pass argument @var{P},
+class switching is allowed}; however, you can not specify any external delay
+(i.e., @var{Z} must be zero) and all servers must be fixed-rate or infinite-server nodes (@code{@var{m}(k) @leq{} 1} for all @math{k}).
+
+ at item r
+ at code{@var{r}(c)} is the reference station for class @math{c}.
+If omitted, station 1 is the reference station for all classes.
+See @command{qncmvisits}.
+
+ at 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
+ at math{M/M/m}--FCFS center with @code{@var{m}(k)} identical servers.
+Default is @code{@var{m}(k)=1} for each @math{k}.
+
+ at item Z
+ at code{@var{Z}(c)} is the class @math{c} external delay (think time);
+ at code{@var{Z}(c) @geq{} 0}. Default is 0. This parameter can not be
+used if you pass a routing matrix as the second parameter of
+ at code{qncmmva}.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+If @math{k} is a FCFS, LCFS-PR or PS node (@code{@var{m}(k) @geq{}
+1}), then @code{@var{U}(c,k)} is the class @math{c} utilization at
+center @math{k}, @math{0 @leq{} U(c,k) @leq{} 1}. 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) =
+ at var{X}(c,k)*@var{S}(c,k)}. In this case the value of
+ at code{@var{U}(c,k)} may be greater than one.
+
+ at item R
+ at code{@var{R}(c,k)} is the class @math{c} response time at
+center @math{k}. The class @math{c} @emph{residence time}
+at center @math{k} is @code{@var{R}(c,k) * @var{C}(c,k)}.
+The total class @math{c} system response time
+is @code{dot(@var{R}, @var{V}, 2)}.
+
+ at item Q
+ at 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,:))}.
+
+ at item X
+ at code{@var{X}(c,k)} is the class @math{c} throughput at
+center @math{k}. The class @math{c} throughput can be computed
+as @code{@var{X}(c,1) / @var{V}(c,1)}.
+
+ at end table
+
+ at quotation Note on numerical stability
+In presence of load-dependent servers (e.g., if @code{@var{m}(i)>1}
+for some @math{i}), the MVA algorithm is known to be numerically
+unstable. Generally this problem manifests itself as negative
+response times or utilizations. This is not a problem with the
+ at code{queueing} toolbox, but with the Mean Value Analysis algorithm,
+and therefore has currently no easy workaround (aoart from using a
+different solution technique, if available). This function prints a
+warning if it detects numerical problems; you can disable the warning
+with the command @code{warning("off", "qn:numerical-instability")}.
+ at end quotation
+
+ at seealso{qnclosed, qncmmvaapprox, qncmvisits}
+
+ at end deftypefn
+
+
+ at 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{qncmmvaap} function, which implements
+the approximate MVA algorithm. Note however that @command{qncmmvaap}
+will only provide approximate results.
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent 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. @uref{http://doi.acm.org/10.1145/322186.322195, 10.1145/322186.322195}
+
+ at auindex Reiser, M.
+ at 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").
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+ at auindex Lazowska, E. D.
+ at auindex Zahorjan, J.
+ at auindex Graham, G. S.
+ at auindex Sevcik, K. C.
+
+ at c
+ at c Approximate MVA, with Bard-Schweitzer approximation
+ at c
+ at anchor{doc-qncmmvaap}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmvaap (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmvaap (@var{N}, @var{S}, @var{V}, @var{m})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol}, @var{iter_max})
+
+ at cindex Mean Value Analysys (MVA), approximate
+ at cindex MVA, approximate
+ at cindex closed network, multiple classes
+ at cindex multiclass network, closed
+
+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
+ at tex
+$$Q_i({\bf N}-{\bf 1}_c) \approx {n-1 \over n} Q_i({\bf N})$$
+ at end tex
+ at 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}:
+
+ at example
+ at group
+Q_i(N-1c) ~ (n-1)/n Q_i(N)
+ at end group
+ at end example
+ at 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.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+ at code{@var{N}(c)} is the number of
+class @math{c} requests in the system (@code{@var{N}(c)>0}).
+
+ at item S
+ at 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}).
+
+ at item V
+ at 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}).
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at service center
+ at 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.
+
+ at item Z
+ at code{@var{Z}(c)} is the class @math{c} external delay. Default
+is 0.
+
+ at item tol
+Stopping tolerance (@code{@var{tol}>0}). The algorithm stops if
+the queue length computed on two subsequent iterations are less than
+ at var{tol}. Default is @math{10^{-5}}.
+
+ at 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.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at 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
+ at 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)}
+
+ at item R
+ at code{@var{R}(c,k)} is the response
+time of class @math{c} requests at service center @math{k}.
+
+ at item Q
+ at code{@var{Q}(c,k)} is the average number of
+class @math{c} requests at service center @math{k}.
+
+ at item X
+ at code{@var{X}(c,k)} is the class @math{c}
+throughput at service center @math{k}.
+
+ at end table
+
+ at seealso{qncmmva}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent 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.
+
+ at auindex Bard, Y.
+
+ at noindent P. Schweitzer, @cite{Approximate Analysis of Multiclass Closed
+Networks of Queues}, Proc. Int. Conf. on Stochastic Control and
+Optimization, jun 1979, pp. 25--29.
+
+ at 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.
+
+ at auindex Lazowska, E. D.
+ at auindex Zahorjan, J.
+ at auindex Graham, G. S.
+ at auindex Sevcik, K. C.
+
+ at subsection Mixed Networks
+
+ at c
+ at c MVA for mixed networks
+ at c
+ at anchor{doc-qnmix}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnmix (@var{lambda}, @var{N}, @var{S}, @var{V}, @var{m})
+
+ at cindex Mean Value Analysys (MVA)
+ at cindex mixed network
+
+Solution of mixed queueing networks through MVA. The network consists
+of @math{K} service centers (single-server or delay centers) and
+ at 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.
+
+ at quotation Note
+In this implementation class switching is @strong{not} allowed. Each
+customer class @emph{must} correspond to an independent chain.
+ at end quotation
+
+If the network is made of open or closed classes only, then this
+function calls @code{qnom} or @code{qncmmva}
+respectively, and prints a warning message.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+ at itemx N
+For each customer chain @math{c}:
+
+ at itemize
+
+ at 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;
+
+ at item If @math{c} is an open chain,
+ at code{@var{lambda}(c)>0} is the arrival rate of class @math{c}
+requests and @code{@var{N}(c)} must be zero;
+
+ at end itemize
+
+ at noindent In other words, for each class @math{c} the following must hold:
+
+ at example
+(@var{lambda}(c)>0 && @var{N}(c)==0) || (@var{lambda}(c)==0 && @var{N}(c)>0)
+ at end example
+
+ at item S
+ at code{@var{S}(c,k)} is the mean class @math{c} service time at center
+ at math{k}, @code{@var{S}(c,k) @geq{} 0}. For FCFS nodes, service times
+must be class-independent.
+
+ at item V
+ at code{@var{V}(c,k)} is the average number of visits of class @math{c}
+customers to center @math{k} (@code{@var{V}(c,k) @geq{} 0}).
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at 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 center
+is assumed to be of type @math{M/M/1}-FCFS. Queueing discipline for
+single-server nodes can be FCFS, PS or LCFS-PR.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+ at code{@var{U}(c,k)} is class @math{c} utilization at center @math{k}.
+
+ at item R
+ at code{@var{R}(c,k)} is class @math{c} response time at center @math{k}.
+
+ at item Q
+ at code{@var{Q}(c,k)} is the average number of
+class @math{c} requests at center @math{k}.
+
+ at item X
+ at code{@var{X}(c,k)} is class @math{c} throughput at center @math{k}.
+
+ at end table
+
+ at seealso{qncmmva, qncm}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at 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 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.
+
+ at auindex Lazowska, E. D.
+ at auindex Zahorjan, J.
+ at auindex Graham, G. S.
+ at auindex Sevcik, K. C.
+
+ at noindent Herb Schwetman, @cite{Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models}, Technical Report
+ at uref{http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-355.pdf,
+CSD-TR-355}, Department of Computer Sciences, Purdue University, feb
+15, 1982,
+
+ at auindex Schwetman, H.
+
+ at c
+ at c
+ at c
+ at node Generic Algorithms
+ at section Generic Algorithms
+
+The @code{queueing} package provides a high-level function
+ at command{qnsolve} for analyzing QN models. @command{qnsolve} takes as
+input a high-level description of the queueing model, and delegates
+the actual solution of the model to one of the lower-level
+function. @command{qnsolve} supports single or multiclass models, but at
+the moment only product-form networks can be analyzed. For non
+product-form networks @xref{Non Product-Form QNs}.
+
+ at command{qnsolve} accepts two input parameters. The first one is the list
+of nodes, encoded as an Octave @emph{cell array}. The second parameter
+is the vector of visit ratios @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
+ at command{qnmknode} function.
+
+ at anchor{doc-qnmknode}
+
+
+ at deftypefn {Function File} {@var{Q} =} qnmknode (@var{"m/m/m-fcfs"}, @var{S})
+ at deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"m/m/m-fcfs"}, @var{S}, @var{m})
+ at deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"m/m/1-lcfs-pr"}, @var{S})
+ at deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"-/g/1-ps"}, @var{S})
+ at deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"-/g/1-ps"}, @var{S}, @var{s2})
+ at deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"-/g/inf"}, @var{S})
+ at deftypefnx {Function File} {@var{Q} =} qnmknode (@var{"-/g/inf"}, @var{S}, @var{s2})
+
+Creates a node; this function can be used together with
+ at 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.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item S
+Mean service time.
+
+ at itemize
+
+ at item If @math{S} is a scalar,
+it is assumed to be a load-independent, class-independent service time.
+
+ at 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.
+
+ at 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. 
+
+ at item Finally, if @var{S} is a two-dimensional matrix, then
+ at code{@var{S}(c,n)} is assumed to be the class @math{c} service time
+when there are @math{n} requests at the node.
+
+ at end itemize
+
+ at item m
+Number of identical servers at the node. Default is @code{@var{m}=1}.
+
+ at item s2
+Squared coefficient of variation for the service time. Default is 1.0.
+
+ at end table
+
+The returned struct @var{Q} should be considered opaque to the client.
+
+ at c The returned struct @var{Q} has the following fields:
+
+ at c @table @var
+
+ at c @item Q.node
+ at c (String) type of the node; valid values are @code{"m/m/m-fcfs"}, 
+ at c @code{"-/g/1-lcfs-pr"}, @code{"-/g/1-ps"} (Processor-Sharing) 
+ at c and @code{"-/g/inf"} (Infinite Server, or delay center).
+
+ at c @item Q.S
+ at c Average service time. If @code{@var{Q}.S} is a vector, then
+ at c @code{@var{Q}.S(i)} is the average service time at that node
+ at c if there are @math{i} requests.
+
+ at c @item Q.m
+ at c Number of identical servers at a @code{"m/m/m-fcfs"}. Default is 1.
+
+ at c @item Q.c
+ at c Number of customer classes. Default is 1.
+
+ at c @end table
+
+ at seealso{qnsolve}
+
+ at end deftypefn
+
+
+After the network has been defined, it is possible to solve it using
+ at command{qnsolve}.
+
+ at anchor{doc-qnsolve}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnsolve (@var{"closed"}, @var{N}, @var{QQ}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnsolve (@var{"closed"}, @var{N}, @var{QQ}, @var{V}, @var{Z})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnsolve (@var{"open"}, @var{lambda}, @var{QQ}, @var{V})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnsolve (@var{"mixed"}, @var{lambda}, @var{N}, @var{QQ}, @var{V})
+
+High-level function for analyzing QN models.
+
+ at itemize
+
+ at item For @strong{closed} networks, the following server types are
+supported: @math{M/M/m}--FCFS, @math{-/G/\infty}, @math{-/G/1}--LCFS-PR,
+ at math{-/G/1}--PS and load-dependent variants.
+
+ at item For @strong{open} networks, the following server types are supported:
+ at 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.
+
+ at item For @strong{mixed} networks, the following server types are supported:
+ at math{M/M/1}--FCFS, @math{-/G/\infty} and @math{-/G/1}--PS. General
+load-dependent nodes are @emph{not} supported.
+
+ at end itemize
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at 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
+ at math{c}.
+
+ at item lambda
+External arrival rate (scalar) for open networks. For single-class
+networks, @var{lambda} must be a scalar. For multiclass networks,
+ at code{@var{lambda}(c)} is the class @math{c} overall arrival rate.
+
+ at 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.
+
+ at item Z
+External delay ("think time") for closed networks. Default 0.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at 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
+ at code{@var{U}(i)} is the @emph{traffic intensity} defined as
+ at code{@var{X}(i)*@var{S}(i)}.
+
+ at item R
+ at code{@var{R}(i)} is the average response time of service center @math{i}.
+
+ at item Q
+ at code{@var{Q}(i)} is the average number of customers in service center
+ at math{i}.
+
+ at item X
+ at code{@var{X}(i)} is the throughput of service center @math{i}.
+
+ at end table
+
+Note that for multiclass networks, the computed results are per-class
+utilization, response time, number of customers and throughput:
+ at code{@var{U}(c,k)}, @code{@var{R}(c,k)}, @code{@var{Q}(c,k)},
+ at code{@var{X}(c,k)},
+
+ at end deftypefn
+
+
+ at 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:
+
+ at itemize
+
+ at 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;
+
+ at item Node 2 is a @math{-/G/1}--PS node, with service times
+ at math{S_{1, 2} = 0.4} for class 1, and @math{S_{2, 2} = 0.6} for class 2
+requests;
+
+ at item Node 3 is a @math{-/G/\infty} node (delay center), with service
+times @math{S_{1, 3}=1} and @math{S_{2, 3}=2} for class 1 and 2
+respectively.
+
+ at end itemize
+
+After defining the per-class visit count @var{V} such that
+ at 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:
+
+ at example
+
+
+ at 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 );
+ at end verbatim
+ at end example
+
+ at anchor{doc-qnclosed}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnclosed (@var{N}, @var{S}, @var{V}, @dots{})
+
+ at cindex closed network, single class
+ at cindex closed network, multiple classes
+
+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
+ at code{qncsemva}, @code{qncsmvald} or @code{qncmmva}.
+
+ at itemize
+
+ at 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{qncsmva} which supports load-independent
+service centers. If @var{S} is a matrix, @code{@var{S}(k,i)} is the
+average service time at center @math{k} when @math{i=1, @dots{}, N}
+jobs are present; in this case, the network is analyzed with the
+ at code{qncmmvald} function.
+
+ at 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{qncmmva} function.
+
+ at end itemize
+
+ at seealso{qncsmva, qncsmvald, qncmmva}
+
+ at end deftypefn
+
+
+ at noindent @strong{EXAMPLE}
+
+ at example
+ at 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(size(S));             # All centers are single-server
+ Z = 2;                         # External delay
+ N = 15;                        # Maximum population to consider
+ V = qncsvisits(P);             # Compute number of visits
+ X_bsb_lower = X_bsb_upper = X_ab_lower = X_ab_upper = X_mva = zeros(1,N);
+ for n=1:N
+   [X_bsb_lower(n) X_bsb_upper(n)] = qncsbsb(n, S, V, m, Z);
+   [X_ab_lower(n) X_ab_upper(n)] = qncsaba(n, S, V, m, 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]); legend("location","southeast");
+ xlabel("Number of Requests n"); ylabel("System Throughput X(n)");
+ at end verbatim
+ at end example
+
+ at anchor{doc-qnopen}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnopen (@var{lambda}, @var{S}, @var{V}, @dots{})
+
+ at 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}.
+
+ at seealso{qnos, qnom}
+
+ at end deftypefn
+
+
+
+ at c
+ at c
+ at c
+ at node Bounds Analysis
+ at section Bounds Analysis
+
+ at c
+ at anchor{doc-qnosaba}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnosaba (@var{lambda}, @var{D})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnosaba (@var{lambda}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnosaba (@var{lambda}, @var{S}, @var{V}, @var{m})
+
+ at cindex bounds, asymptotic
+ at cindex open network
+
+Compute Asymptotic Bounds for open, single-class networks.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Arrival rate of requests (@code{@var{lambda} @geq{} 0}).
+
+ at item D
+ at code{@var{D}(k)} is the service demand at center @math{k}.
+(@code{@var{D}(k) @geq{} 0} for all @math{k}).
+
+ at item S
+ at code{@var{S}(k)} is the mean service time at center @math{k}.
+(@code{@var{S}(k) @geq{} 0} for all @math{k}).
+
+ at item V
+ at code{@var{V}(k)} is the mean number of visits to center @math{k}.
+(@code{@var{V}(k) @geq{} 0} for all @math{k}).
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at center @math{k}.
+This function only supports @math{M/M/1} queues, therefore
+ at var{m} must be @code{ones(size(S))}. 
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at item Xu
+Lower and upper bounds on the system throughput. @var{Xl} is
+always set to @math{0} since there can be no lower bound on the
+throughput of open networks.
+
+ at item Rl
+ at item Ru
+Lower and upper bounds on the system response time. @var{Ru}
+is always set to @code{+inf} since there can be no upper bound on the
+throughput of open networks.
+
+ at end table
+
+ at seealso{qnopenmultiab}
+
+ at end deftypefn
+
+
+ at anchor{doc-qnomaba}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnomaba (@var{lambda}, @var{D})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Rl}] =} qnomaba (@var{lambda}, @var{S}, @var{V})
+
+ at cindex bounds, asymptotic
+ at cindex open network
+ at cindex multiclass network, open
+
+Compute Asymptotic Bounds for open, multiclass networks.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+ at code{@var{lambda}(c)} is the class @math{c} arrival rate to the
+system.
+
+ at item D
+ at code{@var{D}(c, k)} is class @math{c} service demand 
+at center @math{k}. (@code{@var{D}(c, k) @geq{} 0} for all
+ at math{k}).
+
+ at item S
+ at code{@var{S}(c, k)} is the mean service time of class @math{c}
+requests at center @math{k}. (@code{@var{S}(c, k) @geq{} 0} for all
+ at math{k}).
+
+ at item V
+ at code{@var{V}(c, k)} is the mean number of visits of class @math{c}
+requests at center @math{k}. (@code{@var{V}(c, k) @geq{} 0} for all
+ at math{k}).
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at item Xu
+Per-class lower and upper throughput bounds. For example,
+ at code{@var{Xu}(c)} is the upper bound for class @math{c} throughput.
+ at code{Xl} is always @math{0} since there can be no lower bound
+on the throughput of open networks.
+
+ at item Rl
+ at item Ru
+Per-class lower and upper response time bounds. 
+ at code{Ru} is always @code{+inf} since there can be no upper bound
+on the response time of open networks.
+
+ at end table
+
+ at seealso{qnombsb}
+
+ at end deftypefn
+
+
+ at c
+ at anchor{doc-qncsaba}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsaba (@var{N}, @var{D})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsaba (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsaba (@var{N}, @var{S}, @var{V}, @var{m})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsaba (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+
+ at cindex bounds, asymptotic
+ at cindex asymptotic bounds
+ at cindex closed network, single class
+
+Compute Asymptotic Bounds for throughput and response time of closed, single-class networks.
+
+Single-server and infinite-server nodes are supported.
+Multiple-server nodes and general load-dependent servers are not
+supported.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+number of requests in the system (scalar, @code{@var{N}>0}).
+
+ at item D
+ at code{@var{D}(k)} is the service demand at center @math{k}
+(@code{@var{D}(k) @geq{} 0}).
+
+ at item S
+ at code{@var{S}(k)} is the mean service time at center @math{k}
+(@code{@var{S}(k) @geq{} 0}).
+
+ at item V
+ at code{@var{V}(k)} is the average number of visits to center
+ at math{k} (@code{@var{V}(k) @geq{} 0}).
+
+ at item m
+ at 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
+ at code{@var{m}(k) < 1}, center @math{k} is a delay center (IS);
+if @code{@var{m}(k) = 1}, center @math{k} is a M/M/1-FCFS server.
+This function does not support multiple-server nodes. Default
+is 1.
+
+ at item Z
+External delay (@code{@var{Z} @geq{} 0}). Default is 0.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at itemx Xu
+Lower and upper system throughput bounds.
+
+ at item Rl
+ at itemx Ru
+Lower and upper response time bounds.
+
+ at end table
+
+ at seealso{qncmaba}
+
+ at end deftypefn
+
+
+ at anchor{doc-qncmaba}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmaba (@var{N}, @var{D})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmaba (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmaba (@var{N}, @var{S}, @var{V}, @var{m})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmaba (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+
+ at cindex bounds, asymptotic
+ at cindex asymptotic bounds
+ at cindex closed network
+ at cindex multiclass network, closed
+ at cindex closed multiclass network
+
+Compute Asymptotic Bounds for multiclass networks.
+Single-server and infinite-server nodes are supported.
+Multiple-server nodes and general load-dependent servers are not
+supported.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+ at code{@var{N}(c)} is the number of class @math{c} requests in the system.
+
+ at item D
+ at code{@var{D}(c, k)} is class @math{c} service demand
+at center @math{k} (@code{@var{D}(c,k) @geq{} 0}).
+
+ at item S
+ at code{@var{S}(c, k)} is the mean service time of class @math{c}
+requests at center @math{k} (@code{@var{S}(c,k) @geq{} 0}).
+
+ at item V
+ at 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}).
+
+ at item m
+ at 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
+ at code{@var{m}(k) < 1}, center @math{k} is a delay center (IS);
+if @code{@var{m}(k) = 1}, center @math{k} is a M/M/1-FCFS server.
+This function does not support multiple-server nodes. Default
+is 1.
+
+ at item Z
+ at code{@var{Z}(c)} is class @math{c} external delay
+(@code{@var{Z}(c) @geq{} 0}). Default is 0.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at itemx Xu
+Lower and upper class @math{c} throughput bounds.
+
+ at item Rl
+ at itemx Ru
+Lower and upper class @math{c} response time bounds.
+
+ at end table
+
+ at seealso{qnclosedsingleab}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at 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").
+
+ at auindex Lazowska, E. D.
+ at auindex Zahorjan, J.
+ at auindex Graham, G. S.
+ at auindex Sevcik, K. C.
+
+ at c
+ at anchor{doc-qnosbsb}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnosbsb (@var{lambda}, @var{D})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnosbsb (@var{lambda}, @var{S}, @var{V})
+
+ at cindex bounds, balanced system
+ at cindex open network
+
+Compute Balanced System Bounds for single-class, open networks.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda 
+overall arrival rate to the system (scalar). Abort if
+ at code{@var{lambda} < 0 }
+
+ at item D
+ at code{@var{D}(k)} is the service demand at center @math{k}.
+(@code{@var{D}(k) @geq{} 0} for all @math{k}).
+
+ at item S
+ at code{@var{S}(k)} is the mean service time at center @math{k}.
+(@code{@var{S}(k) @geq{} 0} for all @math{k}).
+
+ at item V
+ at code{@var{V}(k)} is the mean number of visits at center @math{k}.
+(@code{@var{V}(k) @geq{} 0} for all @math{k}).
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at center @math{k}.
+This function only supports @math{M/M/1} queues, therefore
+ at var{m} must be @code{ones(size(S))}. 
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at item Xu
+Lower and upper bounds on the system throughput. @var{Xl} is always
+set to @math{0}, since there can be no lower bound on open
+networks throughput.
+
+ at item Rl
+ at itemx Ru
+Lower and upper bounds on the system response time.
+
+ at end table
+
+ at seealso{qnosaba}
+
+ at end deftypefn
+
+
+ at c
+ at anchor{doc-qncsbsb}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsbsb (@var{N}, @var{D})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsbsb (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsbsb (@var{N}, @var{S}, @var{V}, @var{m})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsbsb (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+
+ at cindex bounds, balanced system
+ at cindex closed network, single class
+ at cindex balanced system bounds
+
+Compute Balanced System Bounds on system throughput and response time for closed, single-class networks.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+number of requests in the system (scalar).
+
+ at item D
+ at code{@var{D}(k)} is the service demand at center @math{k}
+(@code{@var{D}(k) @geq{} 0}).
+
+ at item S
+ at code{@var{S}(k)} is the mean service time at center @math{k}
+(@code{@var{S}(k) @geq{} 0}).
+
+ at item V
+ at code{@var{V}(k)} is the average number of visits to center
+ at math{k} (@code{@var{V}(k) @geq{} 0}). Default is 1.
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at center @math{k}. This
+function supports @code{@var{m}(k) = 1} only (sing-eserver FCFS
+nodes). This option is left for compatibility with
+ at code{qncsaba}, Default is 1.
+
+ at item Z
+External delay (@code{@var{Z} @geq{} 0}). Default is 0.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at itemx Xu
+Lower and upper bound on the system throughput.
+
+ at item Rl
+ at itemx Ru
+Lower and upper bound on the system response time.
+
+ at end table
+
+ at seealso{qncmbsb}
+
+ at end deftypefn
+
+
+ at anchor{doc-qncmbsb}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmbsb (@var{N}, @var{D})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmbsb (@var{N}, @var{S}, @var{V})
+
+ at cindex bounds, balanced system
+ at cindex balanced system bounds
+ at cindex multiclass network, closed
+ at cindex closed multiclass network
+
+Compute Balanced System Bounds for multiclass networks.
+Only single-server nodes are supported.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+ at code{@var{N}(c)} is the number of class @math{c} requests in the system.
+
+ at item D
+ at code{@var{D}(c, k)} is class @math{c} service demand 
+at center @math{k} (@code{@var{D}(c,k) @geq{} 0}).
+
+ at item S
+ at code{@var{S}(c, k)} is the mean service time of class @math{c}
+requests at center @math{k} (@code{@var{S}(c,k) @geq{} 0}).
+
+ at item V
+ at 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}). 
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at itemx Xu
+Lower and upper class @math{c} throughput bounds.
+
+ at item Rl
+ at itemx Ru
+Lower and upper class @math{c} response time bounds.
+
+ at end table
+
+ at seealso{qnclosedsinglebsb}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at 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.4 ("Balanced Systems Bounds").
+
+ at auindex Lazowska, E. D.
+ at auindex Zahorjan, J.
+ at auindex Graham, G. S.
+ at auindex Sevcik, K. C.
+
+ at anchor{doc-qncmcb}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmcb (@var{N}, @var{D})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmcb (@var{N}, @var{S}, @var{V})
+
+ at cindex multiclass network, closed
+ at cindex closed multiclass network
+ at cindex bounds, composite
+ at cindex composite bounds
+
+Composite Bound (CB) on throughput and response time for closed multiclass networks.
+
+This function implements the Composite Bound Method described in T.
+Kerola, @cite{The Composite Bound Method (CBM) for Computing
+Throughput Bounds in Multiple Class Environments}, Technical Report
+CSD-TR-475, Purdue University, march 13, 1984 (revised august 27,
+1984).
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+ at code{@var{N}(c)} is the number of class @math{c} requests in the system.
+
+ at item D
+ at code{@var{D}(c, k)} is class @math{c} service demand
+at center @math{k} (@code{@var{S}(c,k) @geq{} 0}).
+
+ at item S
+ at code{@var{S}(c, k)} is the mean service time of class @math{c}
+requests at center @math{k} (@code{@var{S}(c,k) @geq{} 0}).
+
+ at item V
+ at 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}).
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at itemx Xu
+Lower and upper class @math{c} throughput bounds.
+
+ at item Rl
+ at itemx Ru
+Lower and upper class @math{c} response time bounds.
+
+ at end table
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent Teemu Kerola, @cite{The Composite Bound Method (CBM) for Computing
+Throughput Bounds in Multiple Class Environments}, Technical Report
+CSD-TR-475, Department of Computer Sciences, Purdue University, mar 13,
+1984 (Revised aug 27, 1984).
+
+ at auindex Kerola, T.
+
+ at c
+ at anchor{doc-qncspb}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncspb (@var{N}, @var{D} )
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncspb (@var{N}, @var{S}, @var{V} )
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncspb (@var{N}, @var{S}, @var{V}, @var{m} )
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncspb (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z} )
+
+ at cindex bounds, PB
+ at cindex PB bounds
+ at cindex closed network, single class
+
+Compute PB Bounds (C. H. Hsieh and S. Lam, 1987) for single-class,
+closed networks.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+number of requests in the system (scalar). Must be @code{@var{N} > 0}.
+
+ at item D
+ at code{@var{D}(k)} is the service demand of service center @math{k}
+(@code{@var{D}(k) @geq{} 0}).
+
+ at item S
+ at code{@var{S}(k)} is the mean service time at center @math{k}
+(@code{@var{S}(k) @geq{} 0}).
+
+ at item V
+ at code{@var{V}(k)} is the visit ratio to center @math{k}
+(@code{@var{V}(k) @geq{} 0}).
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at center @math{k}.
+This function only supports @math{M/M/1} queues, therefore
+ at var{m} must be @code{ones(size(S))}. 
+
+ at item Z
+external delay (think time, @code{@var{Z} @geq{} 0}). Default 0.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at itemx Xu
+Lower and upper bounds on the system throughput.
+
+ at item Rl
+ at itemx Ru
+Lower and upper bounds on the system response time.
+
+ at end table
+
+ at seealso{qncsaba, qbcsbsb, qncsgb}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+The original paper describing PB Bounds is C. H. Hsieh and S. Lam,
+ at 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.
+
+ at auindex Hsieh, C. H
+ at auindex Lam, S.
+ at auindex Casale, G.
+ at auindex Muntz, R. R.
+ at auindex Serazzi, G.
+
+ at c
+ at anchor{doc-qncsgb}
+
+
+ at deftypefn {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}, @var{Ql}, @var{Qu}] =} qncsgb (@var{N}, @var{D})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}, @var{Ql}, @var{Qu}] =} qncsgb (@var{N}, @var{S}, @var{V})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}, @var{Ql}, @var{Qu}] =} qncsgb (@var{N}, @var{S}, @var{V}, @var{m})
+ at deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}, @var{Ql}, @var{Qu}] =} qncsgb (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+
+ at cindex bounds, geometric
+ at cindex geometric bounds
+ at cindex closed network
+
+Compute Geometric Bounds (GB) on system throughput, system response time and server queue lenghts for closed, single-class networks.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item N
+number of requests in the system (scalar, @code{@var{N} > 0}).
+
+ at item D
+ at code{@var{D}(k)} is the service demand of service center @math{k}
+(@code{@var{D}(k) @geq{} 0}).
+
+ at item S
+ at code{@var{S}(k)} is the mean service time at center @math{k}
+(@code{@var{S}(k) @geq{} 0}).
+
+ at item V
+ at code{@var{V}(k)} is the visit ratio to center @math{k}
+(@code{@var{V}(k) @geq{} 0}).
+
+ at item m
+ at code{@var{m}(k)} is the number of servers at center @math{k}.
+This function only supports @math{M/M/1} queues, therefore
+ at var{m} must be @code{ones(size(S))}. 
+
+ at item Z
+external delay (think time, @code{@var{Z} @geq{} 0}). Default 0.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item Xl
+ at 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)
+
+ at item Rl
+ at itemx Ru
+Lower and upper bound on the system response time. These bounds
+are derived from @var{Xl} and @var{Xu} using Little's Law:
+ at code{@var{Rl} = @var{N} / @var{Xu} - @var{Z}}, 
+ at code{@var{Ru} = @var{N} / @var{Xl} - @var{Z}}
+
+ at item Ql
+ at itemx Qu
+ at 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}.
+
+ at end table
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent G. Casale, R. R. Muntz, G. Serazzi,
+ at cite{Geometric Bounds: a Non-Iterative Analysis Technique for Closed
+Queueing Networks}, IEEE Transactions on Computers, 57(6):780-794,
+June 2008. @uref{http://doi.ieeecomputersociety.org/10.1109/TC.2008.37, 10.1109/TC.2008.37}
+
+ at auindex Casale, G.
+ at auindex Muntz, R. R.
+ at auindex Serazzi, G.
+
+In this implementation we set @math{X^+} and @math{X^-} as the upper
+and lower Asymptotic Bounds as computed by the @command{qncsab}
+function, respectively.
+
+
+ at c
+ at c Examples
+ at c
+
+ at node QN Analysis Examples
+ at section QN Analysis Examples
+
+In this section we illustrate with a few examples how the
+ at code{queueing} package can be used to evaluate queueing network
+models. Further examples can be found in the demo blocks of the
+functions described in this section, and can be accessed with the
+ at code{demo @emph{function}} Octave command.
+
+ at subsection Closed, Single Class Network
+
+We now give a simple example on how the queueing package can be used
+to analyze a closed network. Let us consider again the network shown
+in @ref{fig:qn_closed_single}. We denote with @math{S_k} the average
+service time at center @math{k}, @math{k=1, 2, 3}. We use @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{\bf P}. Specifically, a request completing service at
+center @math{i} is enqueued at center @math{j} with probability
+ at math{P_{i, j}}.  We use the following routing matrix:
+
+ at iftex
+ at tex
+$$
+P = \pmatrix{ 0 & 0.3 & 0.7 \cr
+              1 & 0 & 0 \cr
+              1 & 0 & 0 }
+$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+    / 0  0.3  0.7 \
+P = | 1  0    0   |
+    \ 1  0    0   /
+ at end example
+ at end ifnottex
+
+The network above can be analyzed with the @command{qnclosed} function
+ at pxref{doc-qnclosed}. @command{qnclosed} requires the following
+parameters:
+
+ at table @var
+
+ at item N
+Number of requests in the network (since we are considering a closed
+network, the number of requests is fixed)
+
+ at item S
+Array of average service times at the centers: @code{@var{S}(k)} is
+the average service time at center @math{k}.
+
+ at item V
+Array of visit ratios: @code{@var{V}(k)} is the average number of
+visits to center @math{k}.
+
+ at end table
+
+We can compute @math{V_k} from the routing probability matrix
+ at math{P_{i, j}} using the @command{qncsvisits} function
+ at pxref{doc-qncsvisits}.  We can analyze the network for a given
+population size @math{N} (for example, @math{N=10}) as follows:
+
+ at example
+ at group
+ at kbd{N = 10;}
+ at kbd{S = [1 2 0.8];}
+ at kbd{P = [0 0.3 0.7; 1 0 0; 1 0 0];}
+ at kbd{V = qncsvisits(P);}
+ at 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
+ at end group
+ at end example
+
+The output of @command{qnclosed} includes the vector of utilizations
+ at 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
+ at pxref{doc-qnsolve}. @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.
+
+ at example
+ at group
+ at kbd{Q1 = qnmknode( "m/m/m-fcfs", 1 );}
+ at kbd{Q2 = qnmknode( "m/m/m-fcfs", 2 );}
+ at kbd{Q3 = qnmknode( "m/m/m-fcfs", 0.8 );}
+ at end group
+ at 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
+ at 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
+ at math{m=1} (single-server node).
+
+Now, the network can be analyzed as follows:
+
+ at example
+ at group
+ at kbd{N = 10;}
+ at kbd{V = [1 0.3 0.7];}
+ at 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
+ at end group
+ at end example
+
+ at subsection Open, Single Class Network
+
+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:
+
+ at iftex
+ at tex
+$$
+P = \pmatrix{ 0 & 0.3 & 0.5 \cr
+              1 & 0 & 0 \cr
+              1 & 0 & 0 }
+$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+    / 0  0.3  0.5 \
+P = ! 1  0    0   |
+    \ 1  0    0   /
+ at end example
+ at 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}. We use the
+ at command{qnosvisits} function as follows:
+
+ at example
+ at group
+ at kbd{P = [0 0.3 0.5; 1 0 0; 1 0 0];}
+ at kbd{lambda = [0.15 0 0];}
+ at kbd{V = qnosvisits(P, lambda)}
+   @result{} V = 5.00000 1.50000 2.50000
+ at end group
+ at end example
+
+ at noindent where @code{@var{lambda}(k)} is the arrival rate at center @math{k},
+and @var{P} is the routing matrix. Assuming the same service times as
+in the previous example, the network can be analyzed with the
+ at command{qnopen} function @pxref{doc-qnopen}, as follows:
+
+ at example
+ at group
+ at kbd{S = [1 2 0.8];}
+ at 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
+ at end group
+ at 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:
+
+ at example
+ at group
+ at kbd{Q1 = qnmknode( "m/m/m-fcfs", 1 );}
+ at kbd{Q2 = qnmknode( "m/m/m-fcfs", 2 );}
+ at kbd{Q3 = qnmknode( "m/m/m-fcfs", 0.8 );}
+ at kbd{lambda = [0.15 0 0];}
+ at 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
+ at end group
+ at end example
+
+ at subsection Closed Multiclass Network/1
+
+The following example is taken from Herb Schwetman, @cite{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.
+
+We consider the following multiclass QN with three servers and two classes
+
+ at float Figure,fig:apl
+ at center @image{qn_closed_multi_apl}
+ at end float
+
+Servers 1 and 2 (labeled @emph{APL} and @emph{IMS}, respectively) are
+infinite server nodes; server 3 (labeled @emph{SYS}) is Processor
+Sharing (PS). Mean service times are given in the following table:
+
+ at multitable @columnfractions .15 .15 .15 .15
+ at headitem @tab  APL @tab IMS @tab SYS
+ at item Class 1 @tab 1 @tab - @tab 0.025
+ at item Class 2 @tab - @tab 15 @tab 0.500
+ at end multitable
+
+There is no class switching. If  we assume a population of 15 requests
+for  class 1,  and  5 requests  for class  2,  then the  model can  be
+analyzed as follows:
+
+ at example
+ at verbatim
+ S = [1 0 .025; 0 15 .5];
+ P = zeros(2,3,2,3);
+ P(1,1,1,3) = P(1,3,1,1) = 1;
+ P(2,2,2,3) = P(2,3,2,2) = 1;
+ V = qncmvisits(P,[3 3]); # reference station is station 3
+ N = [15 5];
+ m = [-1 -1 1];
+ [U R Q X] = qncmmva(N,S,V,m)
+ at end verbatim
+  @result{}
+U =
+
+   14.32312    0.00000    0.35808
+    0.00000    4.70699    0.15690
+
+R =
+
+    1.00000    0.00000    0.04726
+    0.00000   15.00000    0.93374
+
+Q =
+
+   14.32312    0.00000    0.67688
+    0.00000    4.70699    0.29301
+
+X =
+
+   14.32312    0.00000   14.32312
+    0.00000    0.31380    0.31380
+ at end example
+
+ at subsection Closed Multiclass Network/2
+
+The following example is taken from M. Marzolla, @cite{The qnetworks
+Toolbox: A Software Package for Queueing Networks Analysis}, Technical
+Report
+ at uref{http://www.informatica.unibo.it/ricerca/technical-report/2010/UBLCS-2010-04,
+UBLCS-2010-04}, Department of Computer Science, University of Bologna,
+Italy, February 2010.
+
+
+ at float Figure,fig:web_model
+ at center @image{qn_web_model,3in}
+ at caption{Three-tier enterprise system model}
+ at end float
+
+The model shown in @ref{fig:web_model} shows a three-tier enterprise system
+with @math{K=6} service centers. The first tier contains the 
+ at emph{Web server} (node 1), which is responsible for generating Web pages and
+transmitting them to clients. The application logic is implemented by
+nodes 2 and 3, and the storage tier is made of nodes 4--6.The system
+is subject to two workload classes, both represented as closed
+populations of @math{N_1} and @math{N_2} requests, respectively. Let
+ at math{D_{c, k}} denote the service demand of class @math{c} requests
+at center @math{k}. We use the parameter values:
+
+ at multitable @columnfractions .2 .33 .1 .1
+ at headitem Serv. no. @tab  Name @tab Class 1 @tab Class 2
+ at item 1 @tab Web Server    @tab 12 @tab 2
+ at item 2 @tab App. Server 1 @tab 14 @tab 20
+ at item 3 @tab App. Server 2 @tab 23 @tab 14
+ at item 4 @tab DB Server 1   @tab 20 @tab 90
+ at item 5 @tab DB Server 2   @tab 80 @tab 30
+ at item 6 @tab DB Server 3   @tab 31 @tab 33
+ at end multitable
+
+We set the total number of requests to 100, that is @math{N_1 + N_2 =
+N = 100}, and we study how different population mixes @math{(N_1,
+N_2)} affect the system throughput and response time. Let
+ at math{\beta_1 \in (0, 1)} denote the fraction of class 1 requests:
+ at math{N_1 = \beta_1 N}, @math{N_2 = (1-\beta_1)N}. The following
+Octave code defines the model for @math{\beta_1 = 0.1}:
+
+ at example
+ at group
+N = 100;     # total population size
+beta1 = 0.1; # fraction of class 1 reqs.
+S = [12 14 23 20 80 31; \
+      2 20 14 90 30 33 ];
+V = ones(size(S));
+pop = [fix(beta1*N) N-fix(beta1*N)];
+[U R Q X] = qncmmva(pop, S, V);
+ at end group
+ at end example
+
+The @command{qncmmva(pop, S, V)} function invocation (line 7)
+uses the multiclass MVA algorithm to compute per-class utilizations
+ at math{U_{c, k}}, response times @math{R_{c,k}}, mean queue lengths
+ at math{Q_{c,k}} and throughputs @math{X_{c,k}} at each service center
+ at math{k}, given a population vector @var{pop}, mean service times
+ at var{S} and visit ratios @var{V}. Since we are given the service
+demands @math{D_{c, k} = S_{c, k} V_{c,k}}, but function
+ at command{qncmmva()} requires separate service times and visit
+ratios, we set the service times equal to the demands (line 3--4), and
+all visit ratios equal to one (line 5). Overall class and system
+throughputs and response times can also be computed:
+
+ at example
+ at group
+X1 = X(1,1) / V(1,1)     # class 1 throughput
+        @result{} X1 =  0.0044219
+X2 = X(2,1) / V(2,1)     # class 2 throughput
+        @result{} X2 =  0.010128
+XX = X1 + X2             # system throughput
+        @result{} XX =  0.014550
+R1 = dot(R(1,:), V(1,:)) # class 1 resp. time
+        @result{} R1 =  2261.5
+R2 = dot(R(2,:), V(2,:)) # class 2 resp. time
+        @result{} R2 =  8885.9
+RR = N / XX              # system resp. time
+        @result{} RR =  6872.7
+ at end group
+ at end example
+
+ at code{dot(X,Y)} computes the dot product of two vectors.
+ at code{R(1,:)} is the first row of matrix @var{R} and @code{V(1,:)} is
+the first row of matrix @var{V}, so @code{dot(R(1,:), V(1,:))}
+computes @math{\sum_k R_{1,k} V_{1,k}}.
+
+ at float Figure,fig:web
+ at center @image{web,3in}
+ at caption{Throughput and Response Times as a function of the population mix}
+ at end float
+
+We can also compute the system power @math{\Phi = X / R}, which
+defines how efficiently resources are being used: high values of
+ at math{\Phi} denote the desirable situation of high throughput and low
+response time. @ref{fig:power} shows @math{\Phi} as a function of
+ at math{\beta_1}. We observe a ``plateau'' of the global system power,
+corresponding to values of @math{\beta_1} which approximately lie
+between @math{0.3} and @math{0.7}. The per-class power exhibits an
+interesting (although not completely surprising) pattern, where the
+class with higher population exhibits worst efficiency as it produces
+higher contention on the resources.
+
+ at float Figure,fig:power
+ at center @image{power,3in}
+ at caption{System Power as a function of the population mix}
+ at end float
+
+ at subsection Closed Multiclass Network/3
+
+We now consider an example of multiclass network with class switching.
+The example is taken from @ref{Sch82}, and is shown in Figure
+ at ref{fig:class_switching}.
+
+ at float Figure,fig:class_switching
+ at center @image{qn_closed_multi_cs,3in}
+ at caption{Multiclass Model with Class Switching}
+ at end float
+
+The system consists of three devices and two job classes. The CPU node
+is a PS server, while the two nodes labeled I/O are FCFS. Class 1 mean
+service time at the CPU is 0.01; class 2 mean service time at the CPU
+is 0.05. The mean service time at node 2 is 0.1, and is
+class-independent. Similarly, the mean service time at node 3 is
+0.07. Jobs in class 1 leave the CPU and join class 2 with probability
+0.1; jobs of class 2 leave the CPU and join class 1 with probability
+0.2. There are @math{N=3} jobs, which are initially allocated to class
+1. However, note that since class switching is allowed, the total
+number of jobs in each class does not remain constant; however the
+total number of jobs does.
+
+ at example
+ at verbatim
+ 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];
+ [U R Q X] = qncmmva(N, S, P)
+ at end verbatim
+  @result{}
+U =
+
+   0.12609   0.61784   0.25218
+   0.31522   0.13239   0.31522
+
+R =
+
+   0.014653   0.133148   0.163256
+   0.073266   0.133148   0.163256
+
+Q =
+
+   0.18476   1.17519   0.41170
+   0.46190   0.25183   0.51462
+
+X =
+
+   12.6089    8.8262    2.5218
+    6.3044    1.8913    3.1522
+ at end example
diff --git a/doc/references.texi b/doc/references.texi
new file mode 100644
index 0000000..dfce4d0
--- /dev/null
+++ b/doc/references.texi
@@ -0,0 +1,141 @@
+ at c This file has been automatically generated from references.txi
+ at c by proc.m. Do not edit this file, all changes will be lost
+
+ at c -*- texinfo -*-
+
+ at c Copyright (C) 2012 Moreno Marzolla
+ at c
+ at c This file is part of the queueing package.
+ at c
+ at c The queueing package is free software; you can redistribute it
+ at c and/or modify it under the terms of the GNU General Public License
+ at c as published by the Free Software Foundation; either version 3 of
+ at c the License, or (at your option) any later version.
+ at c
+ at c The queueing package is distributed in the hope that it will be
+ at c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ at c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ at c GNU General Public License for more details.
+ at c
+ at c You should have received a copy of the GNU General Public License
+ at c along with the queueing package; see the file COPYING.  If not, see
+ at c <http://www.gnu.org/licenses/>.
+
+ at node References
+ at chapter References
+
+ at table @asis
+
+ at item [Aky88]
+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.  DOI @uref{http://dx.doi.org/10.1109/32.4663, 10.1109/32.4663}
+
+ at item [Bar79]
+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.
+
+ at item [BCMP75]
+F. Baskett, K. Mani Chandy, R. R. Muntz, and F. G. Palacios. 1975. @cite{Open, Closed, and Mixed Networks of Queues with Different Classes of Customers}. J. ACM 22, 2 (April 1975), 248—260, DOI @uref{http://doi.acm.org/10.1145/321879.321887, 10.1145/321879.321887}
+
+ at item [BGMT98]
+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.
+
+ at item [Buz73]
+J. 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. DOI @uref{http://doi.acm.org/10.1145/362342.362345, 10.1145/362342.362345}
+
+ at item [C08]
+G. Casale, @cite{A note on stable flow-equivalent aggregation in
+closed networks}. Queueing Syst. Theory Appl., 60:193–-202, December
+2008, DOI @uref{http://dx.doi.org/10.1007/s11134-008-9093-6,
+10.1007/s11134-008-9093-6}
+
+ at item [CMS08]
+G. Casale, R. R. Muntz, G. Serazzi,
+ at cite{Geometric Bounds: a Non-Iterative Analysis Technique for Closed
+Queueing Networks}, IEEE Transactions on Computers, 57(6):780-794,
+June 2008. DOI @uref{http://doi.ieeecomputersociety.org/10.1109/TC.2008.37, 10.1109/TC.2008.37}
+
+ at item @anchor{GrSn97}[GrSn97]
+C. M. Grinstead, J. L. Snell, (July 1997). @cite{Introduction
+to Probability}. American Mathematical Society. ISBN 978-0821807491;
+this excellent textbook is @uref{http://www.dartmouth.edu/~chance/teaching_aids/books_articles/probability_book/amsbook.mac.pdf, available in PDF format}
+and can be used under the terms of the @uref{http://www.gnu.org/copyleft/fdl.html, GNU Free Documentation License (FDL)}
+
+ at item [Jac04]
+J. R. Jackson, @cite{Jobshop-Like Queueing Systems}, Vol. 50, No. 12, Ten Most Influential Titles of "Management Science's" First Fifty Years (Dec., 2004), pp. 1796-1802, @uref{http://www.jstor.org/stable/30046149, available online}
+
+ at item [Jai91]
+R. Jain, @cite{The Art of Computer Systems Performance Analysis},
+Wiley, 1991, p. 577.
+
+ at item [HsLa87]
+C. H. Hsieh and S. Lam,
+ at cite{Two classes of performance bounds for closed queueing networks},
+PEVA, vol. 7, n. 1, pp. 3--30, 1987
+
+ at item [Ker84]
+T. Kerola, @cite{The Composite Bound Method (CBM) for Computing
+Throughput Bounds in Multiple Class Environments},
+ at uref{http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1394&context=cstech,
+Technical Report CSD-TR-475}, Department of Computer Sciences, Purdue
+University, mar 13, 1984 (Revised aug 27, 1984).
+
+ at item [LZGS84]
+E. D. Lazowska, J. Zahorjan, G. Scott Graham, and K. C.
+Sevcik, @cite{Quantitative System Performance: Computer System
+Analysis Using Queueing Network Models}, Prentice Hall,
+1984. @uref{http://www.cs.washington.edu/homes/lazowska/qsp/, available online}.
+
+ at item [ReKo76]
+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. DOI @uref{http://doi.acm.org/10.1145/800200.806187, 10.1145/800200.806187}
+
+ at item [ReLa80]
+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. DOI @uref{http://doi.acm.org/10.1145/322186.322195, 10.1145/322186.322195}
+
+ at item [Sch79]
+P. Schweitzer, @cite{Approximate Analysis of Multiclass Closed Networks of
+Queues}, Proc. Int. Conf. on Stochastic Control and Optimization, jun
+1979, pp. 25—29
+
+ at item [Sch81]
+H. Schwetman, @cite{Some Computational
+Aspects of Queueing Network Models}, @uref{http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-354.pdf, Technical Report CSD-TR-354},
+Department of Computer Sciences, Purdue University, feb, 1981
+(revised).
+
+ at item @anchor{Sch82}[Sch82]
+H. Schwetman, @cite{Implementing the Mean Value Algorithm for the
+Solution of Queueing Network Models}, @uref{http://www.cs.purdue.edu/research/technical_reports/1980/TR%2080-355.pdf, Technical Report CSD-TR-355},
+Department of Computer Sciences, Purdue University, feb 15, 1982.
+
+ at item [Tij03]
+H. C. Tijms, @cite{A first course in stochastic models},
+John Wiley and Sons, 2003, ISBN 0471498807, ISBN 9780471498803,
+DOI @uref{http://dx.doi.org/10.1002/047001363X, 10.1002/047001363X}
+
+ at item [ZaWo81]
+J. Zahorjan and E. Wong, @cite{The solution of separable queueing
+network models using mean value analysis}. SIGMETRICS
+Perform. Eval. Rev. 10, 3 (Sep. 1981), 80-85.
+DOI @uref{http://doi.acm.org/10.1145/1010629.805477, 10.1145/1010629.805477}
+
+ at item [Zeng03]
+G. Zeng, @cite{Two common properties of the erlang-B function, erlang-C function, and Engset blocking function}, Mathematical and Computer Modelling, Volume 37, Issues 12-13, June 2003, Pages 1287-1296 DOI
+ at uref{http://dx.doi.org/10.1016/S0895-7177(03)90040-9, 10.1016/S0895-7177(03)90040-9}
+
+ at end table
diff --git a/doc/singlestation.texi b/doc/singlestation.texi
new file mode 100644
index 0000000..25fb7f8
--- /dev/null
+++ b/doc/singlestation.texi
@@ -0,0 +1,960 @@
+ at c This file has been automatically generated from singlestation.txi
+ at c by proc.m. Do not edit this file, all changes will be lost
+
+ at c -*- texinfo -*-
+
+ at c Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014 Moreno Marzolla
+ at c
+ at c This file is part of the queueing package.
+ at c
+ at c The queueing package is free software; you can redistribute it
+ at c and/or modify it under the terms of the GNU General Public License
+ at c as published by the Free Software Foundation; either version 3 of
+ at c the License, or (at your option) any later version.
+ at c
+ at c The queueing package is distributed in the hope that it will be
+ at c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ at c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ at c GNU General Public License for more details.
+ at c
+ at c You should have received a copy of the GNU General Public License
+ at c along with the queueing package; see the file COPYING.  If not, see
+ at c <http://www.gnu.org/licenses/>.
+
+ at node Single Station Queueing Systems
+ at 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:
+
+ at ifnottex
+ at menu
+* The M/M/1 System::    Single-server queueing station.
+* The M/M/m System::    Multiple-server queueing station.
+* The Erlang-B Formula::
+* The Erlang-C Formula::
+* The Engset Formula::
+* 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.
+ at end menu
+ at end ifnottex
+ at iftex
+ at itemize
+
+ at item @math{M/M/1} single-server queueing station;
+
+ at item @math{M/M/m} multiple-server queueing station;
+
+ at item Asymmetric @math{M/M/m};
+
+ at item @math{M/M/\infty} infinite-server station (delay center);
+
+ at item @math{M/M/1/K} single-server, finite-capacity queueing station;
+
+ at item @math{M/M/m/K} multiple-server, finite-capacity queueing station;
+
+ at item @math{M/G/1} single-server with general service time distribution;
+
+ at item @math{M/H_m/1} single-server with hyperexponential service time distribution.
+
+ at end itemize
+
+ at end iftex
+
+ at c
+ at c M/M/1
+ at c
+ at node The M/M/1 System
+ at 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. Requests arrive according to a Poisson process
+with rate @math{\lambda}; the service time is exponentially
+distributed with average service rate @math{\mu}. The system is stable
+if @math{\lambda < \mu}.
+
+ at anchor{doc-qsmm1}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}] =} qsmm1 (@var{lambda}, @var{mu})
+
+ at cindex @math{M/M/1} system
+
+Compute utilization, response time, average number of requests and throughput for a @math{M/M/1} queue.
+
+ at tex
+The steady-state probability @math{\pi_k} that there are @math{k}
+jobs in the system, @math{k \geq 0}, can be computed as:
+
+$$
+\pi_k = (1-\rho)\rho^k
+$$
+
+where @math{\rho = \lambda/\mu} is the server utilization.
+
+ at end tex
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Arrival rate (@code{@var{lambda} @geq{} 0}).
+
+ at item mu
+Service rate (@code{@var{mu} > @var{lambda}}).
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+Server utilization
+
+ at item R
+Server response time
+
+ at item Q
+Average number of requests in the system
+
+ at item X
+Server throughput. If the system is ergodic (@code{@var{mu} >
+ at var{lambda}}), we always have @code{@var{X} = @var{lambda}}
+
+ at item p0
+Steady-state probability that there are no requests in the system.
+
+ at end table
+
+ at var{lambda} and @var{mu} can be vectors of the same size. In this
+case, the results will be vectors as well.
+
+ at seealso{qsmmm, qsmminf, qsmmmk}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at 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.
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at c
+ at c M/M/m
+ at c
+ at node The M/M/m System
+ at 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 shared
+FCFS 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 requests in the system:
+
+ at iftex
+ at tex
+$$\mu(n) = min(m,n) \mu$$
+ at end tex
+ at end iftex
+ at ifnottex
+ at example
+mu(n) = min(m,n)*mu
+ at end example
+ at end ifnottex
+
+ at noindent where @math{\mu} is the service rate of each individual server.
+
+ at anchor{doc-qsmmm}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}, @var{pm}] =} qsmmm (@var{lambda}, @var{mu})
+ at deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}, @var{pm}] =} qsmmm (@var{lambda}, @var{mu}, @var{m})
+
+ at 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 servers connected to a single FCFS
+queue.
+
+ at tex
+The steady-state probability @math{\pi_k} that there are @math{k}
+jobs in the system, @math{k \geq 0}, can be computed as:
+
+$$
+\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
+}
+$$
+
+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:
+
+$$
+\pi_0 = \left[ \sum_{k=0}^{m-1} { (m\rho)^k \over k! } + { (m\rho)^m \over m!} {1 \over 1-\rho} \right]^{-1}
+$$
+
+ at end tex
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Arrival rate (@code{@var{lambda}>0}).
+
+ at item mu
+Service rate (@code{@var{mu}>@var{lambda}}).
+
+ at item m
+Number of servers (@code{@var{m} @geq{} 1}).
+If omitted, it is assumed @code{@var{m}=1}.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+Service center utilization, @math{U = \lambda / (m \mu)}.
+
+ at item R
+Service center response time
+
+ at item Q
+Average number of requests in the system
+
+ at item X
+Service center throughput. If the system is ergodic, 
+we will always have @code{@var{X} = @var{lambda}}
+
+ at item p0
+Steady-state probability that there are 0 requests in the system
+
+ at item pm
+Steady-state probability that an arriving request has to wait in the
+queue
+
+ at end table
+
+ at var{lambda}, @var{mu} and @var{m} can be vectors of the same size. In this
+case, the results will be vectors as well.
+
+ at seealso{erlangc,qsmm1,qsmminf,qsmmmk}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at 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.
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at c
+ at c Erlang-B
+ at c
+ at node The Erlang-B Formula
+ at section The Erlang-B Formula
+
+ at anchor{doc-erlangb}
+
+
+ at deftypefn {Function File} {@var{B} =} erlangb (@var{A}, @var{m})
+
+ at cindex Erlang-B formula
+
+Compute the value of the Erlang-B formula @math{E_B(A, m)} giving the
+probability that an open system with @math{m} identical servers,
+arrival rate @math{\lambda}, individual service rate @math{\mu}
+and offered load @math{A = \lambda / \mu} has all servers busy.
+
+ at tex
+ at math{E_B(A, m)} is defined as:
+
+$$
+E_B(A, m) = \displaystyle{{A^m \over m!} \left( \sum_{k=0}^m {A^k \over k!} \right) ^{-1}}
+$$
+ at end tex
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item A
+Offered load, defined as @math{A = \lambda / \mu} where
+ at math{\lambda} is the mean arrival rate and @math{\mu} the mean
+service rate of each individual server (real, @math{A > 0}).
+
+ at item m
+Number of identical servers (integer, @math{m @geq{} 1}). Default @math{m = 1}
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item B
+The value @math{E_B(A, m)}
+
+ at end table
+
+ at var{A} or @var{m} can be vectors, and in this case, the results will
+be vectors as well.
+
+ at seealso{qsmmm}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at noindent G. Zeng, @cite{Two common properties of the erlang-B function, erlang-C function, and Engset blocking function}, Mathematical and Computer Modelling, Volume 37, Issues 12-13, June 2003, Pages 1287-1296
+
+ at auindex Zeng, G.
+
+ at c
+ at c Erlang-c
+ at c
+ at node The Erlang-C Formula
+ at section The Erlang-C Formula
+
+ at anchor{doc-erlangc}
+
+
+ at deftypefn {Function File} {@var{C} =} erlangc (@var{A}, @var{m})
+
+ at cindex Erlang-C formula
+
+Compute the steady-state probability @math{E_C(A, m)} that an open
+queueing system with @math{m} identical servers, infinite wating
+space, arrival rate @math{\lambda}, individual service rate
+ at math{\mu} and offered load @math{A = \lambda / \mu} has all the
+servers busy.
+
+ at tex
+ at math{E_C(A, m)} is defined as:
+
+$$
+E_C(A, m) = \displaystyle{ {A^m \over m!} {1 \over 1-\rho} \left( \sum_{k=0}^{m-1} {A^k \over k!} + {A^m \over m!} {1 \over 1 - \rho} \right) ^{-1}}
+$$
+
+where @math{\rho = A / m = \lambda / (m \mu)}.
+ at end tex
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item A Offered load. @math{A = \lambda / \mu} where
+ at math{\lambda} is the mean arrival rate and @math{\mu} the mean
+service rate of each individual server (real, @math{0 < A < m}).
+
+ at item m Number of identical servers (integer, @math{m @geq{} 1}).
+Default @math{m = 1}
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item B The value @math{E_C(A, m)}
+
+ at end table
+
+ at var{A} or @var{m} can be vectors, and in this case, the results will
+be vectors as well.
+
+ at seealso{qsmmm}
+
+ at end deftypefn
+
+
+ at c
+ at c Engset
+ at c
+ at node The Engset Formula
+ at section The Engset Formula
+
+ at anchor{doc-engset}
+
+
+ at deftypefn {Function File} {@var{B} =} engset (@var{A}, @var{m}, @var{n})
+
+ at cindex Engset loss formula
+
+Compute the Engset blocking probability @math{P_b(A, m, n)} for a system
+with a finite population of @math{n} users, @math{m} identical
+servers, no queue, individual service rate @math{\mu}, individual
+arrival rate @math{\lambda} (i.e., the time until a user tries to
+request service is exponentially distributed with mean @math{1 /
+\lambda}), and offered load @math{A = \lambda / \mu}.
+
+ at tex
+ at math{P_b(A, m, n)} is defined for @math{n > m} as:
+
+$$
+P_b(A, m, n) = {{\displaystyle{A^m {n \choose m}}} \over {\displaystyle{\sum_{k=0}^m A^k {n \choose k}}}}
+$$
+
+and is 0 if @math{n @leq{} m}.
+ at end tex
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item A
+Offered load, defined as @math{A = \lambda / \mu} where
+ at math{\lambda} is the mean arrival rate and @math{\mu} the mean
+service rate of each individual server (real, @math{A > 0}).
+
+ at item m
+Number of identical servers (integer, @math{m @geq{} 1}). Default @math{m = 1}
+
+ at item n
+Number of requests (integer, @math{n @geq{} 1}). Default @math{n = 1}
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item B
+The value @math{P_b(A, m, n)}
+
+ at end table
+
+ at var{A}, @var{m} or @math{n} can be vectors, and in this case, the
+results will be vectors as well.
+
+ at seealso{erlangb, erlangc}
+
+ at end deftypefn
+
+
+ at c
+ at c M/M/inf
+ at c
+ at node The M/M/inf System
+ at 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,
+ at 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.
+
+ at anchor{doc-qsmminf}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}] =} qsmminf (@var{lambda}, @var{mu})
+
+Compute utilization, response time, average number of requests and throughput for a @math{M/M/\infty} queue.
+
+The @math{M/M/\infty} system has an infinite number of identical
+servers; this kind of system is always stable for every arrival and
+service rates.
+
+ at cindex @math{M/M/}inf system
+
+ at tex
+The steady-state probability @math{\pi_k} that there are @math{k}
+requests in the system, @math{k @geq{} 0}, can be computed as:
+
+$$
+\pi_k = {1 \over k!} \left( \lambda \over \mu \right)^k e^{-\lambda / \mu}
+$$
+ at end tex
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Arrival rate (@code{@var{lambda}>0}).
+
+ at item mu
+Service rate (@code{@var{mu}>0}).
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at 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.
+
+ at cindex traffic intensity
+
+ at item R
+Service center response time.
+
+ at item Q
+Average number of requests in the system (which is equal to the
+traffic intensity @math{\lambda/\mu}).
+
+ at item X
+Throughput (which is always equal to @code{@var{X} = @var{lambda}}).
+
+ at item p0
+Steady-state probability that there are no requests in the system
+
+ at end table
+
+ at var{lambda} and @var{mu} can be vectors of the same size. In this
+case, the results will be vectors as well.
+
+ at seealso{qsmm1,qsmmm,qsmmmk}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at 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.
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at c
+ at c M/M/1/k
+ at c
+ at node The M/M/1/K System
+ at section The @math{M/M/1/K} System 
+
+In a @math{M/M/1/K} finite capacity system there is a single server
+and there can be at most @math{k \geq 1} jobs at any time (including
+the job currently in service). 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
+ at math{\lambda} and @math{\mu}.
+
+ at anchor{doc-qsmm1k}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}, @var{pK}] =} qsmm1k (@var{lambda}, @var{mu}, @var{K})
+
+ at 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
+ at 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
+ at math{K-1}.
+
+ at tex
+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:
+
+$$
+\pi_k = {(1-a)a^k \over 1-a^{K+1}}
+$$
+
+where @math{a = \lambda/\mu}.
+ at end tex
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Arrival rate (@code{@var{lambda}>0}).
+
+ at item mu
+Service rate (@code{@var{mu}>0}).
+
+ at item K
+Maximum number of requests allowed in the system (@code{@var{K} @geq{} 1}).
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+Service center utilization, which is defined as @code{@var{U} = 1- at var{p0}}
+
+ at item R
+Service center response time
+
+ at item Q
+Average number of requests in the system
+
+ at item X
+Service center throughput
+
+ at item p0
+Steady-state probability that there are no requests in the system
+
+ at item pK
+Steady-state probability that there are @math{K} requests in the system
+(i.e., that the system is full)
+
+ at end table
+
+ at var{lambda}, @var{mu} and @var{K} can be vectors of the
+same size. In this case, the results will be vectors as well.
+
+ at seealso{qsmm1,qsmminf,qsmmm}
+
+ at end deftypefn
+
+
+ at c
+ at c M/M/m/k
+ at c
+ at node The M/M/m/K System
+ at section The @math{M/M/m/K} System 
+
+The @math{M/M/m/K} finite capacity system is similar to the
+ at 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.
+
+ at anchor{doc-qsmmmk}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}, @var{pK}] =} qsmmmk (@var{lambda}, @var{mu}, @var{m}, @var{K})
+
+ at 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
+ at 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.
+
+ at tex
+
+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:
+
+$$
+\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}
+$$
+
+where @math{\rho = \lambda/\mu} is the offered load. The probability
+ at 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:
+
+$$
+\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}
+$$
+
+ at end tex
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Arrival rate (@code{@var{lambda}>0}).
+
+ at item mu
+Service rate (@code{@var{mu}>0}).
+
+ at item m
+Number of servers (@code{@var{m} @geq{} 1}).
+
+ at item K
+Maximum number of requests allowed in the system,
+including those inside the service centers
+(@code{@var{K} @geq{} @var{m}}).
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+Service center utilization
+
+ at item R
+Service center response time
+
+ at item Q
+Average number of requests in the system
+
+ at item X
+Service center throughput
+
+ at item p0
+Steady-state probability that there are no requests in the system.
+
+ at item pK
+Steady-state probability that there are @var{K} requests in the system
+(i.e., probability that the system is full).
+
+ at end table
+
+ at 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.
+
+ at seealso{qsmm1,qsmminf,qsmmm}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at 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.
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at c
+ at c Approximate M/M/m
+ at c
+ at node The Asymmetric M/M/m System
+ at 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.
+
+ at anchor{doc-qsammm}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qsammm (@var{lambda}, @var{mu})
+
+ at 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.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Arrival rate (@code{@var{lambda}>0}).
+
+ at item mu
+ at code{@var{mu}(i)} is the service rate of server
+ at math{i}, @math{1 @leq{} i @leq{} m}.
+The system must be ergodic (@code{@var{lambda} < sum(@var{mu})}).
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+Approximate service center utilization,
+ at math{U = \lambda / ( \sum_i \mu_i )}.
+
+ at item R
+Approximate service center response time
+
+ at item Q
+Approximate number of requests in the system
+
+ at item X
+Approximate service center throughput. If the system is ergodic, 
+we will always have @code{@var{X} = @var{lambda}}
+
+ at end table
+
+ at seealso{qsmmm}
+
+ at end deftypefn
+
+
+ at noindent @strong{REFERENCES}
+
+ at 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
+
+ at auindex Bolch, G.
+ at auindex Greiner, S.
+ at auindex de Meer, H.
+ at auindex Trivedi, K.
+
+ at c
+ at c
+ at c
+ at node The M/G/1 System
+ at section The @math{M/G/1} System 
+
+ at anchor{doc-qsmg1}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}] =} qsmg1 (@var{lambda}, @var{xavg}, @var{x2nd})
+
+ at 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
+ at var{x2nd}. The computations are based on results from L. Kleinrock,
+ at cite{Queuing Systems}, Wiley, Vol 2, and Pollaczek-Khinchine formula.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Arrival rate.
+
+ at item xavg
+Average service time
+
+ at item x2nd
+Second moment of service time distribution
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+Service center utilization
+
+ at item R
+Service center response time
+
+ at item Q
+Average number of requests in the system
+
+ at item X
+Service center throughput
+
+ at item p0
+probability that there is not any request at system
+
+ at end table
+
+ at var{lambda}, @var{xavg}, @var{t2nd} can be vectors of the
+same size. In this case, the results will be vectors as well.
+
+ at seealso{qsmh1}
+
+ at end deftypefn
+
+
+ at c
+ at c
+ at c
+ at node The M/Hm/1 System
+ at section The @math{M/H_m/1} System
+ at anchor{doc-qsmh1}
+
+
+ at deftypefn {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}] =} qsmh1 (@var{lambda}, @var{mu}, @var{alpha})
+
+ at 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:
+
+ at tex
+$$ B(x) = \sum_{j=1}^m \alpha_j(1-e^{-\mu_j x}),\quad x>0 $$
+ at end tex
+
+ at ifnottex
+ at example
+ at group
+       ___ m
+       \
+B(x) =  >  alpha(j) * (1-exp(-mu(j)*x))   x>0
+       /__ 
+           j=1
+ at end group
+ at end example
+ at 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
+ at math{\mu_j}. After completing service at phase @math{j}, for
+some @math{j}, the request exits the system.
+
+ at strong{INPUTS}
+
+ at table @var
+
+ at item lambda
+Arrival rate.
+
+ at item mu
+ at code{@var{mu}(j)} is the phase @math{j} service rate. The total
+number of phases @math{m} is @code{length(@var{mu})}.
+
+ at item alpha
+ at 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}.
+
+ at end table
+
+ at strong{OUTPUTS}
+
+ at table @var
+
+ at item U
+Service center utilization
+
+ at item R
+Service center response time
+
+ at item Q
+Average number of requests in the system
+
+ at item X
+Service center throughput
+
+ at end table
+
+ at end deftypefn
+
+
diff --git a/doc/summary.texi b/doc/summary.texi
new file mode 100644
index 0000000..0e9c50c
--- /dev/null
+++ b/doc/summary.texi
@@ -0,0 +1,179 @@
+ at c This file has been automatically generated from summary.txi
+ at c by proc.m. Do not edit this file, all changes will be lost
+
+ at c -*- texinfo -*-
+
+ at c Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Moreno Marzolla
+ at c
+ at c This file is part of the queueing package.
+ at c
+ at c The queueing package is free software; you can redistribute it
+ at c and/or modify it under the terms of the GNU General Public License
+ at c as published by the Free Software Foundation; either version 3 of
+ at c the License, or (at your option) any later version.
+ at c
+ at c The queueing package is distributed in the hope that it will be
+ at c useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ at c of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ at c GNU General Public License for more details.
+ at c
+ at c You should have received a copy of the GNU General Public License
+ at c along with the queueing package; see the file COPYING.  If not, see
+ at c <http://www.gnu.org/licenses/>.
+
+ at node Summary
+ at chapter Summary
+
+ at menu
+* About the Queueing Package::          What is the Queueing package
+* Contributing Guidelines::             How to contribute
+* Acknowledgements::
+ at end menu
+
+ at node About the Queueing Package
+ at section About the Queueing Package
+
+This document describes the @code{queueing} package for GNU Octave
+(@code{queueing} in short). The @code{queueing} package, 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
+
+ at itemize
+
+ at item Convolution for closed, single-class product-form networks
+with load-dependent service centers;
+
+ at item Exact and approximate Mean Value Analysis (MVA) for single and
+multiple class product-form closed networks;
+
+ at item MVA for mixed, multiple class product-form networks
+with load-independent service centers;
+
+ at item Approximate MVA for closed, single-class networks with blocking
+(MVABLO algorithm by F. Akyildiz);
+
+ at item Asymptotic Bounds, Balanced System Bounds and Geometric Bounds;
+
+ at end itemize
+
+ at noindent @code{queueing} 
+provides functions for analyzing the following kind of single-station
+queueing systems:
+
+ at itemize
+
+ at item @math{M/M/1}
+ at item @math{M/M/m}
+ at item @math{M/M/\infty}
+ at item @math{M/M/1/k} single-server, finite capacity system
+ at item @math{M/M/m/k} multiple-server, finite capacity system
+ at item Asymmetric @math{M/M/m}
+ at item @math{M/G/1} (general service time distribution)
+ at item @math{M/H_m/1} (Hyperexponential service time distribution)
+ at end itemize
+
+Functions for Markov chain analysis are also provided:
+
+ at itemize
+
+ at item Birth-death process;
+ at item Transient and steady-state occupancy probabilities;
+ at item Mean times to absorption;
+ at item Expected sojourn times and time-averaged sojourn times;
+ at item Mean first passage times;
+
+ at end itemize
+
+The @code{queueing} package 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} package in a technical paper, please
+cite it as:
+
+ at 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
+ at end quotation
+
+If you use BibTeX, this is the citation block:
+
+ at verbatim
+ at 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}
+}
+ at end verbatim
+
+An early draft of the paper above is available as Technical Report
+ at 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.
+
+ at node Contributing Guidelines
+ at section Contributing Guidelines
+
+Contributions and bug reports are @emph{always} welcome. If you want
+to contribute to the @code{queueing} package, here are some
+guidelines:
+
+ at itemize
+
+ at item If you are contributing a new function, please embed proper
+documentation within the function itself. The documentation must be in
+ at code{texinfo} format, so that it can be extracted and formatted into
+the printable manual. See the existing functions of the
+ at code{queueing} package for the documentation style.
+
+ at item Make sure that each new function 
+properly checks the validity of its input parameters. For example,
+each function accepting vectors should check whether the dimensions
+match.
+
+ at item Provide bibliographic references for each new algorithm you 
+contribute. If your implementation differs in some way from the
+reference you give, please describe how and why your implementation
+differs. Add references to the @file{doc/references.txi} file.
+
+ at item Include test and demo blocks with your code.
+Test blocks are particularly important, since most algorithms tend to
+be quite tricky to implement correctly. If appropriate, test blocks
+should also verify that the function fails on incorrect input
+parameters.
+
+ at end itemize
+
+Send your contribution to Moreno Marzolla
+(@email{moreno.marzolla@@unibo.it}). If you are just a user of this
+package and find it useful, let me know by dropping me a line. Thanks.
+
+ at node Acknowledgements
+ at section Acknowledgements
+
+The following people (listed in alphabetical order) contributed to the
+ at code{queueing} package, either by providing feedback, reporting bugs
+or contributing code: Philip Carinhas, Phil Colbourn, Diego Didona,
+Yves Durand, Marco Guazzone, Michele Mazzucco, Dmitry Kolesnikov.
diff --git a/doc/web.eps b/doc/web.eps
new file mode 100644
index 0000000..95b8fd1
--- /dev/null
+++ b/doc/web.eps
@@ -0,0 +1,1114 @@
+%!PS-Adobe-2.0 EPSF-2.0
+%%BoundingBox: 26 36 241 193
+%%HiResBoundingBox: 26.394 36.312 240.470 192.930
+%%Title: /tmp/oct-zaqRke.eps
+%%Creator: gnuplot 4.6 patchlevel 0
+%%CreationDate: Mon Mar 10 11:37:19 2014
+%%DocumentFonts: (atend)
+%%EndComments
+%%BeginProlog
+/gnudict 256 dict def
+gnudict begin
+%
+% The following true/false flags may be edited by hand if desired.
+% The unit line width and grayscale image gamma correction may also be changed.
+%
+/Color false def
+/Blacktext false def
+/Solid false def
+/Dashlength 1 def
+/Landscape false def
+/Level1 false def
+/Rounded false def
+/ClipToBoundingBox false def
+/SuppressPDFMark false def
+/TransparentPatterns false def
+/gnulinewidth 5.000 def
+/userlinewidth gnulinewidth def
+/Gamma 1.0 def
+/BackgroundColor {-1.000 -1.000 -1.000} def
+%
+/vshift -66 def
+/dl1 {
+  10.0 Dashlength mul mul
+  Rounded { currentlinewidth 0.75 mul sub dup 0 le { pop 0.01 } if } if
+} def
+/dl2 {
+  10.0 Dashlength mul mul
+  Rounded { currentlinewidth 0.75 mul add } if
+} def
+/hpt_ 31.5 def
+/vpt_ 31.5 def
+/hpt hpt_ def
+/vpt vpt_ def
+/doclip {
+  ClipToBoundingBox {
+    newpath 50 50 moveto 251 50 lineto 251 201 lineto 50 201 lineto closepath
+    clip
+  } if
+} def
+%
+% Gnuplot Prolog Version 4.4 (August 2010)
+%
+%/SuppressPDFMark true def
+%
+/M {moveto} bind def
+/L {lineto} bind def
+/R {rmoveto} bind def
+/V {rlineto} bind def
+/N {newpath moveto} bind def
+/Z {closepath} bind def
+/C {setrgbcolor} bind def
+/f {rlineto fill} bind def
+/g {setgray} bind def
+/Gshow {show} def   % May be redefined later in the file to support UTF-8
+/vpt2 vpt 2 mul def
+/hpt2 hpt 2 mul def
+/Lshow {currentpoint stroke M 0 vshift R 
+	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
+/Rshow {currentpoint stroke M dup stringwidth pop neg vshift R
+	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
+/Cshow {currentpoint stroke M dup stringwidth pop -2 div vshift R 
+	Blacktext {gsave 0 setgray show grestore} {show} ifelse} def
+/UP {dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def
+  /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def} def
+/DL {Color {setrgbcolor Solid {pop []} if 0 setdash}
+ {pop pop pop 0 setgray Solid {pop []} if 0 setdash} ifelse} def
+/BL {stroke userlinewidth 2 mul setlinewidth
+	Rounded {1 setlinejoin 1 setlinecap} if} def
+/AL {stroke userlinewidth 2 div setlinewidth
+	Rounded {1 setlinejoin 1 setlinecap} if} def
+/UL {dup gnulinewidth mul /userlinewidth exch def
+	dup 1 lt {pop 1} if 10 mul /udl exch def} def
+/PL {stroke userlinewidth setlinewidth
+	Rounded {1 setlinejoin 1 setlinecap} if} def
+3.8 setmiterlimit
+% Default Line colors
+/LCw {1 1 1} def
+/LCb {0 0 0} def
+/LCa {0 0 0} def
+/LC0 {1 0 0} def
+/LC1 {0 1 0} def
+/LC2 {0 0 1} def
+/LC3 {1 0 1} def
+/LC4 {0 1 1} def
+/LC5 {1 1 0} def
+/LC6 {0 0 0} def
+/LC7 {1 0.3 0} def
+/LC8 {0.5 0.5 0.5} def
+% Default Line Types
+/LTw {PL [] 1 setgray} def
+/LTb {BL [] LCb DL} def
+/LTa {AL [1 udl mul 2 udl mul] 0 setdash LCa setrgbcolor} def
+/LT0 {PL [] LC0 DL} def
+/LT1 {PL [4 dl1 2 dl2] LC1 DL} def
+/LT2 {PL [2 dl1 3 dl2] LC2 DL} def
+/LT3 {PL [1 dl1 1.5 dl2] LC3 DL} def
+/LT4 {PL [6 dl1 2 dl2 1 dl1 2 dl2] LC4 DL} def
+/LT5 {PL [3 dl1 3 dl2 1 dl1 3 dl2] LC5 DL} def
+/LT6 {PL [2 dl1 2 dl2 2 dl1 6 dl2] LC6 DL} def
+/LT7 {PL [1 dl1 2 dl2 6 dl1 2 dl2 1 dl1 2 dl2] LC7 DL} def
+/LT8 {PL [2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 4 dl2] LC8 DL} def
+/Pnt {stroke [] 0 setdash gsave 1 setlinecap M 0 0 V stroke grestore} def
+/Dia {stroke [] 0 setdash 2 copy vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V closepath stroke
+  Pnt} def
+/Pls {stroke [] 0 setdash vpt sub M 0 vpt2 V
+  currentpoint stroke M
+  hpt neg vpt neg R hpt2 0 V stroke
+ } def
+/Box {stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V closepath stroke
+  Pnt} def
+/Crs {stroke [] 0 setdash exch hpt sub exch vpt add M
+  hpt2 vpt2 neg V currentpoint stroke M
+  hpt2 neg 0 R hpt2 vpt2 V stroke} def
+/TriU {stroke [] 0 setdash 2 copy vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V closepath stroke
+  Pnt} def
+/Star {2 copy Pls Crs} def
+/BoxF {stroke [] 0 setdash exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V closepath fill} def
+/TriUF {stroke [] 0 setdash vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V closepath fill} def
+/TriD {stroke [] 0 setdash 2 copy vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V closepath stroke
+  Pnt} def
+/TriDF {stroke [] 0 setdash vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V closepath fill} def
+/DiaF {stroke [] 0 setdash vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V closepath fill} def
+/Pent {stroke [] 0 setdash 2 copy gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  closepath stroke grestore Pnt} def
+/PentF {stroke [] 0 setdash gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  closepath fill grestore} def
+/Circle {stroke [] 0 setdash 2 copy
+  hpt 0 360 arc stroke Pnt} def
+/CircleF {stroke [] 0 setdash hpt 0 360 arc fill} def
+/C0 {BL [] 0 setdash 2 copy moveto vpt 90 450 arc} bind def
+/C1 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 90 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C2 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 90 180 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C3 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 180 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C4 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 180 270 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C5 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 90 arc
+	2 copy moveto
+	2 copy vpt 180 270 arc closepath fill
+	vpt 0 360 arc} bind def
+/C6 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 90 270 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C7 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 270 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C8 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 270 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C9 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 270 450 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C10 {BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill
+	2 copy moveto
+	2 copy vpt 90 180 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C11 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 180 arc closepath fill
+	2 copy moveto
+	2 copy vpt 270 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C12 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 180 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C13 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 0 90 arc closepath fill
+	2 copy moveto
+	2 copy vpt 180 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/C14 {BL [] 0 setdash 2 copy moveto
+	2 copy vpt 90 360 arc closepath fill
+	vpt 0 360 arc} bind def
+/C15 {BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill
+	vpt 0 360 arc closepath} bind def
+/Rec {newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto
+	neg 0 rlineto closepath} bind def
+/Square {dup Rec} bind def
+/Bsquare {vpt sub exch vpt sub exch vpt2 Square} bind def
+/S0 {BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare} bind def
+/S1 {BL [] 0 setdash 2 copy vpt Square fill Bsquare} bind def
+/S2 {BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def
+/S3 {BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare} bind def
+/S4 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def
+/S5 {BL [] 0 setdash 2 copy 2 copy vpt Square fill
+	exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def
+/S6 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare} bind def
+/S7 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill
+	2 copy vpt Square fill Bsquare} bind def
+/S8 {BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare} bind def
+/S9 {BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare} bind def
+/S10 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill
+	Bsquare} bind def
+/S11 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill
+	Bsquare} bind def
+/S12 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare} bind def
+/S13 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
+	2 copy vpt Square fill Bsquare} bind def
+/S14 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
+	2 copy exch vpt sub exch vpt Square fill Bsquare} bind def
+/S15 {BL [] 0 setdash 2 copy Bsquare fill Bsquare} bind def
+/D0 {gsave translate 45 rotate 0 0 S0 stroke grestore} bind def
+/D1 {gsave translate 45 rotate 0 0 S1 stroke grestore} bind def
+/D2 {gsave translate 45 rotate 0 0 S2 stroke grestore} bind def
+/D3 {gsave translate 45 rotate 0 0 S3 stroke grestore} bind def
+/D4 {gsave translate 45 rotate 0 0 S4 stroke grestore} bind def
+/D5 {gsave translate 45 rotate 0 0 S5 stroke grestore} bind def
+/D6 {gsave translate 45 rotate 0 0 S6 stroke grestore} bind def
+/D7 {gsave translate 45 rotate 0 0 S7 stroke grestore} bind def
+/D8 {gsave translate 45 rotate 0 0 S8 stroke grestore} bind def
+/D9 {gsave translate 45 rotate 0 0 S9 stroke grestore} bind def
+/D10 {gsave translate 45 rotate 0 0 S10 stroke grestore} bind def
+/D11 {gsave translate 45 rotate 0 0 S11 stroke grestore} bind def
+/D12 {gsave translate 45 rotate 0 0 S12 stroke grestore} bind def
+/D13 {gsave translate 45 rotate 0 0 S13 stroke grestore} bind def
+/D14 {gsave translate 45 rotate 0 0 S14 stroke grestore} bind def
+/D15 {gsave translate 45 rotate 0 0 S15 stroke grestore} bind def
+/DiaE {stroke [] 0 setdash vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V closepath stroke} def
+/BoxE {stroke [] 0 setdash exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V closepath stroke} def
+/TriUE {stroke [] 0 setdash vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V closepath stroke} def
+/TriDE {stroke [] 0 setdash vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V closepath stroke} def
+/PentE {stroke [] 0 setdash gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  closepath stroke grestore} def
+/CircE {stroke [] 0 setdash 
+  hpt 0 360 arc stroke} def
+/Opaque {gsave closepath 1 setgray fill grestore 0 setgray closepath} def
+/DiaW {stroke [] 0 setdash vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V Opaque stroke} def
+/BoxW {stroke [] 0 setdash exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V Opaque stroke} def
+/TriUW {stroke [] 0 setdash vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V Opaque stroke} def
+/TriDW {stroke [] 0 setdash vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V Opaque stroke} def
+/PentW {stroke [] 0 setdash gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  Opaque stroke grestore} def
+/CircW {stroke [] 0 setdash 
+  hpt 0 360 arc Opaque stroke} def
+/BoxFill {gsave Rec 1 setgray fill grestore} def
+/Density {
+  /Fillden exch def
+  currentrgbcolor
+  /ColB exch def /ColG exch def /ColR exch def
+  /ColR ColR Fillden mul Fillden sub 1 add def
+  /ColG ColG Fillden mul Fillden sub 1 add def
+  /ColB ColB Fillden mul Fillden sub 1 add def
+  ColR ColG ColB setrgbcolor} def
+/BoxColFill {gsave Rec PolyFill} def
+/PolyFill {gsave Density fill grestore grestore} def
+/h {rlineto rlineto rlineto gsave closepath fill grestore} bind def
+%
+% PostScript Level 1 Pattern Fill routine for rectangles
+% Usage: x y w h s a XX PatternFill
+%	x,y = lower left corner of box to be filled
+%	w,h = width and height of box
+%	  a = angle in degrees between lines and x-axis
+%	 XX = 0/1 for no/yes cross-hatch
+%
+/PatternFill {gsave /PFa [ 9 2 roll ] def
+  PFa 0 get PFa 2 get 2 div add PFa 1 get PFa 3 get 2 div add translate
+  PFa 2 get -2 div PFa 3 get -2 div PFa 2 get PFa 3 get Rec
+  gsave 1 setgray fill grestore clip
+  currentlinewidth 0.5 mul setlinewidth
+  /PFs PFa 2 get dup mul PFa 3 get dup mul add sqrt def
+  0 0 M PFa 5 get rotate PFs -2 div dup translate
+  0 1 PFs PFa 4 get div 1 add floor cvi
+	{PFa 4 get mul 0 M 0 PFs V} for
+  0 PFa 6 get ne {
+	0 1 PFs PFa 4 get div 1 add floor cvi
+	{PFa 4 get mul 0 2 1 roll M PFs 0 V} for
+ } if
+  stroke grestore} def
+%
+/languagelevel where
+ {pop languagelevel} {1} ifelse
+ 2 lt
+	{/InterpretLevel1 true def}
+	{/InterpretLevel1 Level1 def}
+ ifelse
+%
+% PostScript level 2 pattern fill definitions
+%
+/Level2PatternFill {
+/Tile8x8 {/PaintType 2 /PatternType 1 /TilingType 1 /BBox [0 0 8 8] /XStep 8 /YStep 8}
+	bind def
+/KeepColor {currentrgbcolor [/Pattern /DeviceRGB] setcolorspace} bind def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke} 
+>> matrix makepattern
+/Pat1 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke
+	0 4 M 4 8 L 8 4 L 4 0 L 0 4 L stroke}
+>> matrix makepattern
+/Pat2 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 0 0 M 0 8 L
+	8 8 L 8 0 L 0 0 L fill}
+>> matrix makepattern
+/Pat3 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop -4 8 M 8 -4 L
+	0 12 M 12 0 L stroke}
+>> matrix makepattern
+/Pat4 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop -4 0 M 8 12 L
+	0 -4 M 12 8 L stroke}
+>> matrix makepattern
+/Pat5 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop -2 8 M 4 -4 L
+	0 12 M 8 -4 L 4 12 M 10 0 L stroke}
+>> matrix makepattern
+/Pat6 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop -2 0 M 4 12 L
+	0 -4 M 8 12 L 4 -4 M 10 8 L stroke}
+>> matrix makepattern
+/Pat7 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 8 -2 M -4 4 L
+	12 0 M -4 8 L 12 4 M 0 10 L stroke}
+>> matrix makepattern
+/Pat8 exch def
+<< Tile8x8
+ /PaintProc {0.5 setlinewidth pop 0 -2 M 12 4 L
+	-4 0 M 12 8 L -4 4 M 8 10 L stroke}
+>> matrix makepattern
+/Pat9 exch def
+/Pattern1 {PatternBgnd KeepColor Pat1 setpattern} bind def
+/Pattern2 {PatternBgnd KeepColor Pat2 setpattern} bind def
+/Pattern3 {PatternBgnd KeepColor Pat3 setpattern} bind def
+/Pattern4 {PatternBgnd KeepColor Landscape {Pat5} {Pat4} ifelse setpattern} bind def
+/Pattern5 {PatternBgnd KeepColor Landscape {Pat4} {Pat5} ifelse setpattern} bind def
+/Pattern6 {PatternBgnd KeepColor Landscape {Pat9} {Pat6} ifelse setpattern} bind def
+/Pattern7 {PatternBgnd KeepColor Landscape {Pat8} {Pat7} ifelse setpattern} bind def
+} def
+%
+%
+%End of PostScript Level 2 code
+%
+/PatternBgnd {
+  TransparentPatterns {} {gsave 1 setgray fill grestore} ifelse
+} def
+%
+% Substitute for Level 2 pattern fill codes with
+% grayscale if Level 2 support is not selected.
+%
+/Level1PatternFill {
+/Pattern1 {0.250 Density} bind def
+/Pattern2 {0.500 Density} bind def
+/Pattern3 {0.750 Density} bind def
+/Pattern4 {0.125 Density} bind def
+/Pattern5 {0.375 Density} bind def
+/Pattern6 {0.625 Density} bind def
+/Pattern7 {0.875 Density} bind def
+} def
+%
+% Now test for support of Level 2 code
+%
+Level1 {Level1PatternFill} {Level2PatternFill} ifelse
+%
+/Symbol-Oblique /Symbol findfont [1 0 .167 1 0 0] makefont
+dup length dict begin {1 index /FID eq {pop pop} {def} ifelse} forall
+currentdict end definefont pop
+/MFshow {
+   { dup 5 get 3 ge
+     { 5 get 3 eq {gsave} {grestore} ifelse }
+     {dup dup 0 get findfont exch 1 get scalefont setfont
+     [ currentpoint ] exch dup 2 get 0 exch R dup 5 get 2 ne {dup dup 6
+     get exch 4 get {Gshow} {stringwidth pop 0 R} ifelse }if dup 5 get 0 eq
+     {dup 3 get {2 get neg 0 exch R pop} {pop aload pop M} ifelse} {dup 5
+     get 1 eq {dup 2 get exch dup 3 get exch 6 get stringwidth pop -2 div
+     dup 0 R} {dup 6 get stringwidth pop -2 div 0 R 6 get
+     show 2 index {aload pop M neg 3 -1 roll neg R pop pop} {pop pop pop
+     pop aload pop M} ifelse }ifelse }ifelse }
+     ifelse }
+   forall} def
+/Gswidth {dup type /stringtype eq {stringwidth} {pop (n) stringwidth} ifelse} def
+/MFwidth {0 exch { dup 5 get 3 ge { 5 get 3 eq { 0 } { pop } ifelse }
+ {dup 3 get{dup dup 0 get findfont exch 1 get scalefont setfont
+     6 get Gswidth pop add} {pop} ifelse} ifelse} forall} def
+/MLshow { currentpoint stroke M
+  0 exch R
+  Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def
+/MRshow { currentpoint stroke M
+  exch dup MFwidth neg 3 -1 roll R
+  Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def
+/MCshow { currentpoint stroke M
+  exch dup MFwidth -2 div 3 -1 roll R
+  Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def
+/XYsave    { [( ) 1 2 true false 3 ()] } bind def
+/XYrestore { [( ) 1 2 true false 4 ()] } bind def
+Level1 SuppressPDFMark or 
+{} {
+/SDict 10 dict def
+systemdict /pdfmark known not {
+  userdict /pdfmark systemdict /cleartomark get put
+} if
+SDict begin [
+  /Title (/tmp/oct-zaqRke.eps)
+  /Subject (gnuplot plot)
+  /Creator (gnuplot 4.6 patchlevel 0)
+  /Author (marzolla)
+%  /Producer (gnuplot)
+%  /Keywords ()
+  /CreationDate (Mon Mar 10 11:37:19 2014)
+  /DOCINFO pdfmark
+end
+} ifelse
+end
+%%EndProlog
+%%Page: 1 1
+gnudict begin
+gsave
+doclip
+50 50 translate
+0.050 0.050 scale
+0 setgray
+newpath
+(Helvetica) findfont 200 scalefont setfont
+BackgroundColor 0 lt 3 1 roll 0 lt exch 0 lt or or not {BackgroundColor C 1.000 0 0 4030.00 3022.00 BoxColFill} if
+gsave % colour palette begin
+/maxcolors 64 def
+/HSV2RGB {  exch dup 0.0 eq {pop exch pop dup dup} % achromatic gray
+  { /HSVs exch def /HSVv exch def 6.0 mul dup floor dup 3 1 roll sub
+     /HSVf exch def /HSVi exch cvi def /HSVp HSVv 1.0 HSVs sub mul def
+	 /HSVq HSVv 1.0 HSVs HSVf mul sub mul def 
+	 /HSVt HSVv 1.0 HSVs 1.0 HSVf sub mul sub mul def
+	 /HSVi HSVi 6 mod def 0 HSVi eq {HSVv HSVt HSVp}
+	 {1 HSVi eq {HSVq HSVv HSVp}{2 HSVi eq {HSVp HSVv HSVt}
+	 {3 HSVi eq {HSVp HSVq HSVv}{4 HSVi eq {HSVt HSVp HSVv}
+	 {HSVv HSVp HSVq} ifelse} ifelse} ifelse} ifelse} ifelse
+  } ifelse} def
+/Constrain {
+  dup 0 lt {0 exch pop}{dup 1 gt {1 exch pop} if} ifelse} def
+/YIQ2RGB {
+  3 copy -1.702 mul exch -1.105 mul add add Constrain 4 1 roll
+  3 copy -0.647 mul exch -0.272 mul add add Constrain 5 1 roll
+  0.621 mul exch -0.956 mul add add Constrain 3 1 roll } def
+/CMY2RGB {  1 exch sub exch 1 exch sub 3 2 roll 1 exch sub 3 1 roll exch } def
+/XYZ2RGB {  3 copy -0.9017 mul exch -0.1187 mul add exch 0.0585 mul exch add
+  Constrain 4 1 roll 3 copy -0.0279 mul exch 1.999 mul add exch
+  -0.9844 mul add Constrain 5 1 roll -0.2891 mul exch -0.5338 mul add
+  exch 1.91 mul exch add Constrain 3 1 roll} def
+/SelectSpace {ColorSpace (HSV) eq {HSV2RGB}{ColorSpace (XYZ) eq {
+  XYZ2RGB}{ColorSpace (CMY) eq {CMY2RGB}{ColorSpace (YIQ) eq {YIQ2RGB}
+  if} ifelse} ifelse} ifelse} def
+/InterpolatedColor true def
+/grayindex {/gidx 0 def
+  {GrayA gidx get grayv ge {exit} if /gidx gidx 1 add def} loop} def
+/dgdx {grayv GrayA gidx get sub GrayA gidx 1 sub get
+  GrayA gidx get sub div} def 
+/redvalue {RedA gidx get RedA gidx 1 sub get
+  RedA gidx get sub dgdxval mul add} def
+/greenvalue {GreenA gidx get GreenA gidx 1 sub get
+  GreenA gidx get sub dgdxval mul add} def
+/bluevalue {BlueA gidx get BlueA gidx 1 sub get
+  BlueA gidx get sub dgdxval mul add} def
+/interpolate {
+  grayindex grayv GrayA gidx get sub abs 1e-5 le
+    {RedA gidx get GreenA gidx get BlueA gidx get}
+    {/dgdxval dgdx def redvalue greenvalue bluevalue} ifelse} def
+/GrayA [0 .0159 .0317 .0476 .0635 .0794 .0952 .1111 .127 .1429 .1587 .1746 
+  .1905 .2063 .2222 .2381 .254 .2698 .2857 .3016 .3175 .3333 .3492 .3651 
+  .381 .3968 .4127 .4286 .4444 .4603 .4762 .4921 .5079 .5238 .5397 .5556 
+  .5714 .5873 .6032 .619 .6349 .6508 .6667 .6825 .6984 .7143 .7302 .746 
+  .7619 .7778 .7937 .8095 .8254 .8413 .8571 .873 .8889 .9048 .9206 .9365 
+  .9524 .9683 .9841 1 ] def
+/RedA [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .0238 .0873 .1508 
+  .2143 .2778 .3413 .4048 .4683 .5317 .5952 .6587 .7222 .7857 .8492 .9127 
+  .9762 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 .9444 .881 .8175 .754 .6905 .627 
+  .5635 .5 ] def
+/GreenA [0 0 0 0 0 0 0 0 .0079 .0714 .1349 .1984 .2619 .3254 .3889 .4524 
+  .5159 .5794 .6429 .7063 .7698 .8333 .8968 .9603 1 1 1 1 1 1 1 1 1 1 1 1 1 
+  1 1 1 .9603 .8968 .8333 .7698 .7063 .6429 .5794 .5159 .4524 .3889 .3254 
+  .2619 .1984 .1349 .0714 .0079 0 0 0 0 0 0 0 0 ] def
+/BlueA [.5 .5635 .627 .6905 .754 .8175 .881 .9444 1 1 1 1 1 1 1 1 1 1 1 1 1 
+  1 1 1 .9762 .9127 .8492 .7857 .7222 .6587 .5952 .5317 .4683 .4048 .3413 
+  .2778 .2143 .1508 .0873 .0238 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+  0 0 ] def
+/pm3dround {maxcolors 0 gt {dup 1 ge
+	{pop 1} {maxcolors mul floor maxcolors 1 sub div} ifelse} if} def
+/pm3dGamma 1.0 1.5 Gamma mul div def
+/ColorSpace (RGB) def
+Color InterpolatedColor or { % COLOUR vs. GRAY map
+  InterpolatedColor { %% Interpolation vs. RGB-Formula
+    /g {stroke pm3dround /grayv exch def interpolate
+        SelectSpace setrgbcolor} bind def
+  }{
+  /g {stroke pm3dround dup cF7 Constrain exch dup cF5 Constrain exch cF15 Constrain 
+       SelectSpace setrgbcolor} bind def
+  } ifelse
+}{
+  /g {stroke pm3dround pm3dGamma exp setgray} bind def
+} ifelse
+0.500 UL
+LTb
+523 1764 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 1764 M
+[ [({}) 200.0 0.0 true true 0 (0)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 2022 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 2022 M
+[ [({}) 200.0 0.0 true true 0 (0.005)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 2279 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 2279 M
+[ [({}) 200.0 0.0 true true 0 (0.01)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 2536 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 2536 M
+[ [({}) 200.0 0.0 true true 0 (0.015)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 2794 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 2794 M
+[ [({}) 200.0 0.0 true true 0 (0.02)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 1764 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+523 1564 M
+[ [({}) 200.0 0.0 true true 0 (0)]
+] -66.7 MCshow
+0.500 UL
+LTb
+1148 1764 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+1148 1564 M
+[ [({}) 200.0 0.0 true true 0 (0.2)]
+] -66.7 MCshow
+0.500 UL
+LTb
+1772 1764 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+1772 1564 M
+[ [({}) 200.0 0.0 true true 0 (0.4)]
+] -66.7 MCshow
+0.500 UL
+LTb
+2397 1764 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+2397 1564 M
+[ [({}) 200.0 0.0 true true 0 (0.6)]
+] -66.7 MCshow
+0.500 UL
+LTb
+3021 1764 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+3021 1564 M
+[ [({}) 200.0 0.0 true true 0 (0.8)]
+] -66.7 MCshow
+0.500 UL
+LTb
+3646 1764 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+3646 1564 M
+[ [({}) 200.0 0.0 true true 0 (1)]
+] -66.7 MCshow
+0.500 UL
+LTb
+0.500 UL
+LTb
+523 2794 N
+0 -1030 V
+3123 0 V
+0 1030 V
+-3123 0 V
+Z stroke
+LCb setrgbcolor
+-417 2279 M
+currentpoint gsave translate 90 rotate 0 0 moveto
+[ [({}) 200.0 0.0 true true 0 (Throughput)]
+] -66.7 MCshow
+grestore
+LTb
+1.000 UP
+0.500 UL
+LTb
+% Begin plot #1
+2.000 UL
+LT1
+835 1992 M
+147 82 V
+147 87 V
+147 53 V
+147 15 V
+147 3 V
+147 0 V
+147 0 V
+147 0 V
+147 0 V
+147 0 V
+147 1 V
+147 4 V
+147 12 V
+147 16 V
+147 25 V
+147 28 V
+147 29 V
+% End plot #1
+% Begin plot #2
+stroke
+LT2
+835 2286 M
+147 -19 V
+147 -19 V
+147 -12 V
+147 -3 V
+147 -1 V
+147 0 V
+147 0 V
+147 0 V
+147 0 V
+147 0 V
+147 -1 V
+147 -11 V
+147 -33 V
+147 -43 V
+147 -67 V
+147 -74 V
+147 -78 V
+% End plot #2
+% Begin plot #3
+stroke
+LT0
+835 2513 M
+147 65 V
+147 67 V
+147 41 V
+147 12 V
+147 2 V
+147 0 V
+147 0 V
+147 0 V
+147 0 V
+147 0 V
+147 -1 V
+147 -6 V
+147 -21 V
+147 -27 V
+147 -42 V
+147 -46 V
+147 -49 V
+% End plot #3
+1.000 UP
+stroke
+0.500 UL
+LTb
+grestore % colour palette end
+gsave % colour palette begin
+/maxcolors 64 def
+/HSV2RGB {  exch dup 0.0 eq {pop exch pop dup dup} % achromatic gray
+  { /HSVs exch def /HSVv exch def 6.0 mul dup floor dup 3 1 roll sub
+     /HSVf exch def /HSVi exch cvi def /HSVp HSVv 1.0 HSVs sub mul def
+	 /HSVq HSVv 1.0 HSVs HSVf mul sub mul def 
+	 /HSVt HSVv 1.0 HSVs 1.0 HSVf sub mul sub mul def
+	 /HSVi HSVi 6 mod def 0 HSVi eq {HSVv HSVt HSVp}
+	 {1 HSVi eq {HSVq HSVv HSVp}{2 HSVi eq {HSVp HSVv HSVt}
+	 {3 HSVi eq {HSVp HSVq HSVv}{4 HSVi eq {HSVt HSVp HSVv}
+	 {HSVv HSVp HSVq} ifelse} ifelse} ifelse} ifelse} ifelse
+  } ifelse} def
+/Constrain {
+  dup 0 lt {0 exch pop}{dup 1 gt {1 exch pop} if} ifelse} def
+/YIQ2RGB {
+  3 copy -1.702 mul exch -1.105 mul add add Constrain 4 1 roll
+  3 copy -0.647 mul exch -0.272 mul add add Constrain 5 1 roll
+  0.621 mul exch -0.956 mul add add Constrain 3 1 roll } def
+/CMY2RGB {  1 exch sub exch 1 exch sub 3 2 roll 1 exch sub 3 1 roll exch } def
+/XYZ2RGB {  3 copy -0.9017 mul exch -0.1187 mul add exch 0.0585 mul exch add
+  Constrain 4 1 roll 3 copy -0.0279 mul exch 1.999 mul add exch
+  -0.9844 mul add Constrain 5 1 roll -0.2891 mul exch -0.5338 mul add
+  exch 1.91 mul exch add Constrain 3 1 roll} def
+/SelectSpace {ColorSpace (HSV) eq {HSV2RGB}{ColorSpace (XYZ) eq {
+  XYZ2RGB}{ColorSpace (CMY) eq {CMY2RGB}{ColorSpace (YIQ) eq {YIQ2RGB}
+  if} ifelse} ifelse} ifelse} def
+/InterpolatedColor true def
+/grayindex {/gidx 0 def
+  {GrayA gidx get grayv ge {exit} if /gidx gidx 1 add def} loop} def
+/dgdx {grayv GrayA gidx get sub GrayA gidx 1 sub get
+  GrayA gidx get sub div} def 
+/redvalue {RedA gidx get RedA gidx 1 sub get
+  RedA gidx get sub dgdxval mul add} def
+/greenvalue {GreenA gidx get GreenA gidx 1 sub get
+  GreenA gidx get sub dgdxval mul add} def
+/bluevalue {BlueA gidx get BlueA gidx 1 sub get
+  BlueA gidx get sub dgdxval mul add} def
+/interpolate {
+  grayindex grayv GrayA gidx get sub abs 1e-5 le
+    {RedA gidx get GreenA gidx get BlueA gidx get}
+    {/dgdxval dgdx def redvalue greenvalue bluevalue} ifelse} def
+/GrayA [0 .0159 .0317 .0476 .0635 .0794 .0952 .1111 .127 .1429 .1587 .1746 
+  .1905 .2063 .2222 .2381 .254 .2698 .2857 .3016 .3175 .3333 .3492 .3651 
+  .381 .3968 .4127 .4286 .4444 .4603 .4762 .4921 .5079 .5238 .5397 .5556 
+  .5714 .5873 .6032 .619 .6349 .6508 .6667 .6825 .6984 .7143 .7302 .746 
+  .7619 .7778 .7937 .8095 .8254 .8413 .8571 .873 .8889 .9048 .9206 .9365 
+  .9524 .9683 .9841 1 ] def
+/RedA [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .0238 .0873 .1508 
+  .2143 .2778 .3413 .4048 .4683 .5317 .5952 .6587 .7222 .7857 .8492 .9127 
+  .9762 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 .9444 .881 .8175 .754 .6905 .627 
+  .5635 .5 ] def
+/GreenA [0 0 0 0 0 0 0 0 .0079 .0714 .1349 .1984 .2619 .3254 .3889 .4524 
+  .5159 .5794 .6429 .7063 .7698 .8333 .8968 .9603 1 1 1 1 1 1 1 1 1 1 1 1 1 
+  1 1 1 .9603 .8968 .8333 .7698 .7063 .6429 .5794 .5159 .4524 .3889 .3254 
+  .2619 .1984 .1349 .0714 .0079 0 0 0 0 0 0 0 0 ] def
+/BlueA [.5 .5635 .627 .6905 .754 .8175 .881 .9444 1 1 1 1 1 1 1 1 1 1 1 1 1 
+  1 1 1 .9762 .9127 .8492 .7857 .7222 .6587 .5952 .5317 .4683 .4048 .3413 
+  .2778 .2143 .1508 .0873 .0238 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+  0 0 ] def
+/pm3dround {maxcolors 0 gt {dup 1 ge
+	{pop 1} {maxcolors mul floor maxcolors 1 sub div} ifelse} if} def
+/pm3dGamma 1.0 1.5 Gamma mul div def
+/ColorSpace (RGB) def
+Color InterpolatedColor or { % COLOUR vs. GRAY map
+  InterpolatedColor { %% Interpolation vs. RGB-Formula
+    /g {stroke pm3dround /grayv exch def interpolate
+        SelectSpace setrgbcolor} bind def
+  }{
+  /g {stroke pm3dround dup cF7 Constrain exch dup cF5 Constrain exch cF15 Constrain 
+       SelectSpace setrgbcolor} bind def
+  } ifelse
+}{
+  /g {stroke pm3dround pm3dGamma exp setgray} bind def
+} ifelse
+0.500 UL
+LTb
+523 332 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 332 M
+[ [({}) 200.0 0.0 true true 0 (0)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 446 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 446 M
+[ [({}) 200.0 0.0 true true 0 (1000)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 561 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 561 M
+[ [({}) 200.0 0.0 true true 0 (2000)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 675 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 675 M
+[ [({}) 200.0 0.0 true true 0 (3000)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 790 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 790 M
+[ [({}) 200.0 0.0 true true 0 (4000)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 904 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 904 M
+[ [({}) 200.0 0.0 true true 0 (5000)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 1019 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 1019 M
+[ [({}) 200.0 0.0 true true 0 (6000)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 1133 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 1133 M
+[ [({}) 200.0 0.0 true true 0 (7000)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 1248 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 1248 M
+[ [({}) 200.0 0.0 true true 0 (8000)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 1362 M
+88 0 V
+3035 0 R
+-88 0 V
+stroke
+403 1362 M
+[ [({}) 200.0 0.0 true true 0 (9000)]
+] -66.7 MRshow
+0.500 UL
+LTb
+523 332 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+523 132 M
+[ [({}) 200.0 0.0 true true 0 (0)]
+] -66.7 MCshow
+0.500 UL
+LTb
+1148 332 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+1148 132 M
+[ [({}) 200.0 0.0 true true 0 (0.2)]
+] -66.7 MCshow
+0.500 UL
+LTb
+1772 332 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+1772 132 M
+[ [({}) 200.0 0.0 true true 0 (0.4)]
+] -66.7 MCshow
+0.500 UL
+LTb
+2397 332 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+2397 132 M
+[ [({}) 200.0 0.0 true true 0 (0.6)]
+] -66.7 MCshow
+0.500 UL
+LTb
+3021 332 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+3021 132 M
+[ [({}) 200.0 0.0 true true 0 (0.8)]
+] -66.7 MCshow
+0.500 UL
+LTb
+3646 332 M
+0 88 V
+0 942 R
+0 -88 V
+stroke
+3646 132 M
+[ [({}) 200.0 0.0 true true 0 (1)]
+] -66.7 MCshow
+0.500 UL
+LTb
+0.500 UL
+LTb
+523 1362 N
+523 332 L
+3123 0 V
+0 1030 V
+-3123 0 V
+Z stroke
+LCb setrgbcolor
+-297 847 M
+currentpoint gsave translate 90 rotate 0 0 moveto
+[ [({}) 200.0 0.0 true true 0 (Response Time)]
+] -66.7 MCshow
+grestore
+LTb
+LCb setrgbcolor
+2084 -167 M
+[ [({}) 200.0 0.0 true true 0 (Population mix )]
+[(Symbol) 200.0 0.0 true true 0 (b)]
+[({}) 160.0 -60.0 true true 0 (1)]
+[({}) 200.0 0.0 true true 0 ( for Class 1)]
+] -46.7 MCshow
+LTb
+1.000 UP
+0.500 UL
+LTb
+% Begin plot #1
+2.000 UL
+LT1
+LCb setrgbcolor
+2113 895 M
+[ [({}) 200.0 0.0 true true 0 (Class 1)]
+] -66.7 MRshow
+LT1
+2233 895 M
+543 0 V
+835 591 M
+147 7 V
+147 16 V
+147 33 V
+147 40 V
+147 61 V
+147 62 V
+147 51 V
+147 63 V
+147 63 V
+147 62 V
+147 50 V
+147 56 V
+147 39 V
+147 20 V
+147 14 V
+147 8 V
+147 5 V
+% End plot #1
+% Begin plot #2
+stroke
+LT2
+LCb setrgbcolor
+2113 695 M
+[ [({}) 200.0 0.0 true true 0 (Class 2)]
+] -66.7 MRshow
+LT2
+2233 695 M
+543 0 V
+835 1349 M
+147 -10 V
+147 -21 V
+147 -38 V
+147 -43 V
+147 -62 V
+147 -62 V
+147 -51 V
+147 -63 V
+147 -63 V
+147 -63 V
+147 -48 V
+147 -53 V
+147 -36 V
+147 -16 V
+147 -11 V
+147 -7 V
+147 -4 V
+% End plot #2
+% Begin plot #3
+stroke
+LT0
+LCb setrgbcolor
+2113 495 M
+[ [({}) 200.0 0.0 true true 0 (System)]
+] -66.7 MRshow
+LT0
+2233 495 M
+543 0 V
+835 1119 M
+147 -63 V
+147 -55 V
+147 -30 V
+147 -8 V
+147 -1 V
+147 -1 V
+147 0 V
+147 0 V
+147 0 V
+147 1 V
+147 0 V
+147 5 V
+147 14 V
+147 20 V
+147 33 V
+147 41 V
+147 49 V
+% End plot #3
+1.000 UP
+stroke
+0.500 UL
+LTb
+grestore % colour palette end
+stroke
+grestore
+end
+showpage
+%%Trailer
+%%DocumentFonts: Symbol Helvetica
diff --git a/inst/ctmc.m b/inst/ctmc.m
new file mode 100644
index 0000000..56b2b69
--- /dev/null
+++ b/inst/ctmc.m
@@ -0,0 +1,302 @@
+## 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{p0})
+##
+## @cindex Markov chain, continuous time
+## @cindex continuous time Markov chain
+## @cindex Markov chain, state occupancy probabilities
+## @cindex stationary probabilities
+## @cindex CTMC
+##
+## Compute stationary or transient state occupancy probabilities for a continuous-time Markov chain.
+##
+## 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 state space @math{@{1, 2, @dots{},
+## N@}} and @math{N \times N} infinitesimal generator matrix @var{Q}.
+## With three arguments, compute the state occupancy probabilities
+## @var{p}(1), @dots{}, @var{p}(N) that the system is in state @math{i}
+## at time @var{t}, given initial state occupancy probabilities
+## @var{p0}(1), @dots{}, @var{p0}(N) 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}.
+## #var{Q} must satisfy the property that @math{\sum_{j=1}^N Q_{i, j} =
+## 0}
+##
+## @item t
+## Time at which to compute the transient probability (@math{t @geq{}
+## 0}). If omitted, the function computes the steady state occupancy
+## probability vector.
+##
+## @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}. 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{p0}(1), @dots{}, @var{p0}(N).
+##
+## @end table
+##
+## @seealso{dtmc}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function q = ctmc( Q, t, p0 )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin != 1 && nargin != 3 )
+    print_usage();
+  endif
+
+  [N err] = ctmcchkQ(Q);
+
+  ( N>0 ) || ...
+      error(err);
+
+  if ( nargin == 1 ) # steady-state analysis
+
+    ## 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;
+    
+  else # transient analysis
+
+    ( isscalar(t) && t>=0 ) || ...
+        error("t must be a scalar >= 0");
+
+    ( isvector(p0) && length(p0) == N && all(p0>=0) && abs(sum(p0)-1.0)<N*eps ) || ...
+        error( "p0 must be a probability vector" );   
+
+    p0 = p0(:)'; # make p0 a row vector
+
+    q = p0*expm(Q*t);
+
+  endif
+
+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 );
+
+## Example on p. 172 of [Tij03]
+%!test
+%! ll = 0.1;
+%! mu = 100;
+%! eta = 5;
+%! Q = zeros(9,9);
+%! ## 6--1, 7=sleep2 8=sleep1 9=crash
+%! Q(6,5) = 6*ll;
+%! Q(5,4) = 5*ll;
+%! Q(4,3) = 4*ll;
+%! Q(3,2) = 3*ll;
+%! Q(2,1) = 2*ll;
+%! Q(2,7) = mu;
+%! Q(1,9) = ll;
+%! Q(1,8) = mu;
+%! Q(8,9) = ll;
+%! Q(7,8) = 2*ll;
+%! Q(7,6) = eta;
+%! Q(8,6) = eta;
+%! Q -= diag(sum(Q,2));
+%! q0 = zeros(1,9); q0(6) = 1;
+%! q = ctmc(Q,10,q0);
+%! assert( q(9), 0.000504, 1e-6 );
+%! q = ctmc(Q,2,q0);
+%! assert( q, [3.83e-7 1.938e-4 0.0654032 0.2216998 0.4016008 0.3079701 0.0030271 0.0000998 5e-6], 1e-5 );
+%! # Compute probability that no shuttle needs to leave during 10 years
+%! Q(7,:) = Q(8,:) = 0; # make states 7 and 8 absorbing
+%! q = ctmc(Q,10,q0);
+%! assert( 1-sum(q(7:9)), 0.3901, 1e-4 );
+
+%!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 = ctmcmtta(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 = ctmcmtta(Q, p0) / hour;
+%! printf("System MTBF %.2f hours\n",MTBF);
\ No newline at end of file
diff --git a/inst/ctmc_bd.m b/inst/ctmc_bd.m
new file mode 100644
index 0000000..e5ad7c6
--- /dev/null
+++ b/inst/ctmc_bd.m
@@ -0,0 +1,44 @@
+## Copyright (C) 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_bd (@var{b}, @var{d})
+##
+## This function is deprecated. Please use @code{ctmcbd} instead.
+##
+## @seealso{ctmcbd}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function Q = ctmc_bd( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "ctmc_bd is deprecated. Please use ctmcbd instead");
+  endif
+  Q = ctmcbd( varargin{:} );
+endfunction
+%!test
+%! birth = [ 1 1 1 ];
+%! death = [ 2 2 2 ];
+%! Q = ctmc_bd( birth, death );
+%! assert( ctmc(Q), [ 8/15 4/15 2/15 1/15 ], 1e-5 );
diff --git a/inst/ctmc_check_Q.m b/inst/ctmc_check_Q.m
new file mode 100644
index 0000000..a410509
--- /dev/null
+++ b/inst/ctmc_check_Q.m
@@ -0,0 +1,71 @@
+## Copyright (C)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} @var{err}] =} ctmc_check_Q (@var{Q})
+##
+## This function is deprecated. Please use @code{ctmcchkQ} instead
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [result err] = ctmc_check_Q( Q )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "ctmc_check_Q is deprecated. Please use ctmcchkQ instead");
+  endif
+  [result err] = ctmcchkQ( Q );
+endfunction
+%!test
+%! Q = [0];
+%! [result err] = ctmc_check_Q(Q);
+%! assert( result, 1 );
+%! assert( err, "" );
+
+%!test
+%! N = 10;
+%! Q = ctmc_bd(rand(1,N-1),rand(1,N-1));
+%! [result err] = ctmc_check_Q(Q);
+%! assert( result, N );
+%! assert( err, "" );
+
+%!test
+%! Q = [1 2 3; 4 5 6];
+%! [result err] = ctmc_check_Q(Q);
+%! assert( result, 0 );
+%! assert( index(err, "square") > 0 );
+
+%!test
+%! N = 10;
+%! Q = ctmc_bd(rand(1,N-1),rand(1,N-1));
+%! Q(2,1) = -1;
+%! [result err] = ctmc_check_Q(Q);
+%! assert( result, 0 );
+%! assert( index(err, "infinitesimal") > 0 );
+
+%!test
+%! N = 10;
+%! Q = ctmc_bd(rand(1,N-1),rand(1,N-1));
+%! Q(1,1) += 7;
+%! [result err] = ctmc_check_Q(Q);
+%! assert( result, 0 );
+%! assert( index(err, "infinitesimal") > 0 );
diff --git a/inst/ctmc_exps.m b/inst/ctmc_exps.m
new file mode 100644
index 0000000..07b5800
--- /dev/null
+++ b/inst/ctmc_exps.m
@@ -0,0 +1,60 @@
+## 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{t}, @var{p} )
+## @deftypefnx {Function File} {@var{L} =} ctmc_exps (@var{Q}, @var{p})
+##
+## This function is deprecated. Please use @code{ctmcexps} instead.
+##
+## @seealso{ctmcexps}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function L = ctmc_exps( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "ctmc_exps is deprecated. Please use ctmcexps instead");
+  endif
+  L = ctmcexps( varargin{:} );
+endfunction
+%!test
+%! Q = [-1 1; 1 -1];
+%! L = ctmc_exps(Q,10,[1 0]);
+%! L = ctmc_exps(Q,linspace(0,10,100),[1 0]);
+
+%!test
+%! Q = ctmc_bd( [1 2 3], [3 2 1] );
+%! p0 = [1 0 0 0];
+%! t = linspace(0,10,10);
+%! L1 = L2 = zeros(length(t),4);
+%! # compute L using the differential equation formulation
+%! ff = @(x,t) (x(:)'*Q+p0);
+%! fj = @(x,t) (Q);
+%! L1 = lsode( {ff, fj}, zeros(size(p0)), t );
+%! # compute L using ctmc_exps (integral formulation)
+%! for i=1:length(t)
+%!   L2(i,:) = ctmc_exps(Q,t(i),p0);
+%! endfor
+%! assert( L1, L2, 1e-5);
+
diff --git a/inst/ctmc_fpt.m b/inst/ctmc_fpt.m
new file mode 100644
index 0000000..f792442
--- /dev/null
+++ b/inst/ctmc_fpt.m
@@ -0,0 +1,47 @@
+## 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})
+##
+## This function is deprecated. Please use @code{ctmcfpt} instead.
+##
+## @seealso{ctmcfpt}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function result = ctmc_fpt( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "ctmc_fpt is deprecated. Please use ctmcfpt instead");
+  endif
+  result = ctmcfpt( varargin{:} );
+endfunction
+%!test
+%! N = 10;
+%! Q = reshape(1:N^2,N,N);
+%! Q(1:N+1:end) = 0;
+%! Q -= diag(sum(Q,2));
+%! M = ctmc_fpt(Q);
+%! assert( all(diag(M) < 10*eps) );
diff --git a/inst/ctmc_mtta.m b/inst/ctmc_mtta.m
new file mode 100644
index 0000000..8ec65a5
--- /dev/null
+++ b/inst/ctmc_mtta.m
@@ -0,0 +1,64 @@
+## 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})
+##
+## This function is deprecated. Please use @code{ctmcmtta} instead.
+##
+## @seealso{ctmcmtta}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function t = ctmc_mtta( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "ctmc_mtta is deprecated. Please use ctmcmtta instead");
+  endif
+  t = ctmcmtta( varargin{:} );
+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])", "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
+
diff --git a/inst/ctmc_taexps.m b/inst/ctmc_taexps.m
new file mode 100644
index 0000000..689900e
--- /dev/null
+++ b/inst/ctmc_taexps.m
@@ -0,0 +1,48 @@
+## Copyright (C) 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{t}, @var{p})
+## @deftypefnx {Function File} {@var{M} =} ctmc_taexps (@var{Q}, @var{p})
+##
+## This function is deprecated. Please use @code{ctmctaexps} instead.
+##
+## @seealso{ctmctaexps}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function M = ctmc_taexps( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "ctmc_taexps is deprecated. Please use ctmctaexps instead");
+  endif
+  M = ctmctaexps( varargin{:} );
+endfunction
+%!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) );
+%! M = ctmc_taexps(Q, [1 0 0 0]);
+%! assert( sum(M), 1, 10*eps );
diff --git a/inst/ctmcbd.m b/inst/ctmcbd.m
new file mode 100644
index 0000000..074e76f
--- /dev/null
+++ b/inst/ctmcbd.m
@@ -0,0 +1,99 @@
+## 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{Q} =} ctmcbd (@var{b}, @var{d})
+##
+## @cindex Markov chain, continuous time
+## @cindex continuous time Markov chain
+## @cindex CTMC
+## @cindex birth-death process, CTMC
+##
+## Returns the infinitesimal generator matrix @math{Q} for a continuous
+## birth-death process over state space @math{1, 2, @dots{}, N}.
+## @code{@var{b}(i)} is the transition rate from state @math{i} to
+## @math{i+1}, and @code{@var{d}(i)} is the transition rate from state
+## @math{i+1} to state @math{i}, @math{i=1, 2, @dots{}, N-1}.
+##
+## Matrix @math{\bf Q} is therefore defined as:
+##
+## @tex
+## $$ \pmatrix{ -\lambda_1 & \lambda_1 & & & & \cr
+##              \mu_1 & -(\mu_1 + \lambda_2) & \lambda_2 & & \cr
+##              & \mu_2 & -(\mu_2 + \lambda_3) & \lambda_3 & & \cr
+##              \cr
+##              & & \ddots & \ddots & \ddots & & \cr
+##              \cr
+##              & & & \mu_{N-2} & -(\mu_{N-2}+\lambda_{N-1}) & \lambda_{N-1} \cr
+##              & & & & \mu_{N-1} & -\mu_{N-1} }
+## $$
+## @end tex
+## @ifnottex
+##
+## @example
+## @group
+## /                                                          \
+## | -b(1)     b(1)                                           |
+## |  d(1) -(d(1)+b(2))     b(2)                              |
+## |           d(2)     -(d(2)+b(3))        b(3)              |
+## |                                                          |
+## |                ...           ...          ...            |
+## |                                                          |
+## |                       d(N-2)    -(d(N-2)+b(N-1))  b(N-1) |
+## |                                       d(N-1)     -d(N-1) |
+## \                                                          /
+## @end group
+## @end example
+## @end ifnottex
+##
+## @noindent where @math{\lambda_i} and @math{\mu_i} are the birth and
+## death rates, respectively.
+##
+## @seealso{dtmcbd}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function Q = ctmcbd( birth, death )
+
+  if ( nargin != 2 ) 
+    print_usage();
+  endif
+
+  ( isvector( birth ) && isvector( death ) ) || ...
+      error( "birth and death must be vectors" );
+  birth = birth(:); # make birth a column vector
+  death = death(:); # make death a column vector
+  size_equal( birth, death ) || ...
+      error( "birth and death rates must have the same length" );
+  all( birth >= 0 ) || ...
+      error( "birth rates must be >= 0" );
+  all( death >= 0 ) || ...
+      error( "death rates must be >= 0" );
+
+  ## builds the infinitesimal generator matrix
+  Q = diag( birth, 1 ) + diag( death, -1 );
+  Q -= diag( sum(Q,2) );
+endfunction
+%!test
+%! birth = [ 1 1 1 ];
+%! death = [ 2 2 2 ];
+%! Q = ctmcbd( birth, death );
+%! assert( ctmc(Q), [ 8/15 4/15 2/15 1/15 ], 1e-5 );
diff --git a/inst/ctmcchkQ.m b/inst/ctmcchkQ.m
new file mode 100644
index 0000000..a562999
--- /dev/null
+++ b/inst/ctmcchkQ.m
@@ -0,0 +1,91 @@
+## Copyright (C)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} @var{err}] =} ctmcchkQ (@var{Q})
+##
+## @cindex Markov chain, continuous time
+##
+## If @var{Q} is a valid infinitesimal generator matrix, return
+## the size (number of rows or columns) of @var{Q}. If @var{Q} is not
+## an infinitesimal generator matrix, set @var{result} to zero, and
+## @var{err} to an appropriate error string.
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [result err] = ctmcchkQ( Q )
+
+  persistent epsilon = 100*eps;
+
+  if ( nargin != 1 )
+    print_usage();
+  endif
+
+  result = 0;
+  err = "";
+
+  if ( !issquare(Q) )
+    err = "P is not a square matrix";
+    return;
+  endif
+  
+  if (any(Q(~logical(eye(size(Q))))<0) || ... # there is any negative non-diagonal element
+      norm( sum(Q,2), "inf" ) > epsilon )
+    err = "Q is not an infinitesimal generator matrix";
+    return;
+  endif
+
+  result = rows(Q);
+endfunction
+%!test
+%! Q = [0];
+%! [result err] = ctmcchkQ(Q);
+%! assert( result, 1 );
+%! assert( err, "" );
+
+%!test
+%! N = 10;
+%! Q = ctmcbd(rand(1,N-1),rand(1,N-1));
+%! [result err] = ctmcchkQ(Q);
+%! assert( result, N );
+%! assert( err, "" );
+
+%!test
+%! Q = [1 2 3; 4 5 6];
+%! [result err] = ctmcchkQ(Q);
+%! assert( result, 0 );
+%! assert( index(err, "square") > 0 );
+
+%!test
+%! N = 10;
+%! Q = ctmcbd(rand(1,N-1),rand(1,N-1));
+%! Q(2,1) = -1;
+%! [result err] = ctmcchkQ(Q);
+%! assert( result, 0 );
+%! assert( index(err, "infinitesimal") > 0 );
+
+%!test
+%! N = 10;
+%! Q = ctmcbd(linspace(1,N-1,N-1),linspace(1,N-1,N-1));
+%! Q(1,1) += 7;
+%! [result err] = ctmcchkQ(Q);
+%! assert( result, 0 );
+%! assert( index(err, "infinitesimal") > 0 );
diff --git a/inst/ctmcexps.m b/inst/ctmcexps.m
new file mode 100644
index 0000000..1e63b04
--- /dev/null
+++ b/inst/ctmcexps.m
@@ -0,0 +1,182 @@
+## 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} =} ctmcexps (@var{Q}, @var{t}, @var{p} )
+## @deftypefnx {Function File} {@var{L} =} ctmcexps (@var{Q}, @var{p})
+##
+## @cindex Markov chain, continuous time
+## @cindex expected sojourn time, CTMC
+##
+## With three arguments, compute the expected times @code{@var{L}(i)}
+## spent in each state @math{i} during the time interval @math{[0,t]},
+## assuming that the initial occupancy vector is @var{p}. With two
+## arguments, compute the expected time @code{@var{L}(i)} spent in each
+## transient state @math{i} until absorption.
+##
+## @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{1
+## @leq{} i \neq j @leq{} N}. The matrix @var{Q} must also satisfy the
+## condition @math{\sum_{j=1}^N Q_{ij} = 0}.
+##
+## @item t
+## If given, compute the expected sojourn times in @math{[0,t]}
+##
+## @item p
+## Initial occupancy probability vector; @code{@var{p}(i)} is the
+## probability the system is in state @math{i} at time 0, @math{i = 1,
+## @dots{}, N}
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item L
+## If this function is called with three arguments, @code{@var{L}(i)} is
+## the expected time spent in state @math{i} during the interval
+## @math{[0,t]}. If this function is called with two arguments
+## @code{@var{L}(i)} is the expected time spent in transient state
+## @math{i} until absorption; if state @math{i} is absorbing,
+## @code{@var{L}(i)} is zero.
+##
+## @end table
+##
+## @seealso{dtmcexps}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function L = ctmcexps( Q, varargin )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+
+  [N err] = ctmcchkQ(Q);
+
+  (N>0) || ...
+      error(err);
+
+  if ( nargin == 2 )
+    p = varargin{1};
+  else
+    t = varargin{1};
+    p = varargin{2};
+  endif
+
+  ( isvector(p) && length(p) == size(Q,1) && all(p>=0) && abs(sum(p)-1.0)<epsilon ) || ...
+      error( "p must be a probability vector" );
+
+  p = p(:)'; # make p a row vector
+
+  if ( nargin == 3 ) # non-absorbing case
+    if ( isscalar(t) )
+      (t >= 0 ) || ...
+	  error( "t must be >= 0" );
+      ## F(x) are the transient state occupancy probabilities at time x
+      ## F(x) = p*expm(Q*x) (see function ctmc()).
+      F = @(x) (p*expm(Q*x));
+      L = quadv(F,0,t);
+    else
+      ## FIXME: deprecate this?
+      ( isvector(t) && abs(t(1)) < epsilon ) || ...
+	  error( "t must be a vector, and t(1) must be 0.0" );
+      t = t(:)'; # make tt a row vector
+      ff = @(x,t) (x(:)'*Q+p);
+      fj = @(x,t) (Q);
+      L = lsode( {ff, fj}, zeros(size(p)), t );
+    endif
+  else # absorbing case
+
+    ## Identify transient states. If all states are transient, then
+    ## raise an error since we can't deal with non-absorbing chains
+    ## here.
+
+    N = rows(Q);
+    tr = find( any( abs(Q) > epsilon, 2 ) );
+    if ( length( tr ) == N )
+      error( "There are no absorbing states" );
+    endif
+    
+    QN = Q(tr,tr);
+    pN = p(tr);
+    LN = -pN*inv(QN);
+    L = zeros(1,N);
+    L(tr) = LN;
+  endif
+endfunction
+%!test
+%! Q = [-1 1; 1 -1];
+%! L = ctmcexps(Q,10,[1 0]);
+%! L = ctmcexps(Q,linspace(0,10,100),[1 0]);
+
+%!test
+%! Q = ctmcbd( [1 2 3], [3 2 1] );
+%! p0 = [1 0 0 0];
+%! t = linspace(0,10,10);
+%! L1 = L2 = zeros(length(t),4);
+%! # compute L using the differential equation formulation
+%! ff = @(x,t) (x(:)'*Q+p0);
+%! fj = @(x,t) (Q);
+%! L1 = lsode( {ff, fj}, zeros(size(p0)), t );
+%! # compute L using ctmcexps (integral formulation)
+%! for i=1:length(t)
+%!   L2(i,:) = ctmcexps(Q,t(i),p0);
+%! endfor
+%! assert( L1, L2, 1e-5);
+
+%!demo
+%! lambda = 0.5;
+%! N = 4;
+%! b = lambda*[1:N-1];
+%! d = zeros(size(b));
+%! Q = ctmcbd(b,d);
+%! t = linspace(0,10,100);
+%! p0 = zeros(1,N); p0(1)=1;
+%! L = zeros(length(t),N);
+%! for i=1:length(t)
+%!   L(i,:) = ctmcexps(Q,t(i),p0);
+%! endfor
+%! plot( t, L(:,1), ";State 1;", "linewidth", 2, ...
+%!       t, L(:,2), ";State 2;", "linewidth", 2, ...
+%!       t, L(:,3), ";State 3;", "linewidth", 2, ...
+%!       t, L(:,4), ";State 4;", "linewidth", 2 );
+%! legend("location","northwest");
+%! xlabel("Time");
+%! ylabel("Expected sojourn time");
+
+%!demo
+%! lambda = 0.5;
+%! N = 4;
+%! b = lambda*[1:N-1];
+%! d = zeros(size(b));
+%! Q = ctmcbd(b,d);
+%! p0 = zeros(1,N); p0(1)=1;
+%! L = ctmcexps(Q,p0);
+%! disp(L);
diff --git a/inst/ctmcfpt.m b/inst/ctmcfpt.m
new file mode 100644
index 0000000..93c8c97
--- /dev/null
+++ b/inst/ctmcfpt.m
@@ -0,0 +1,116 @@
+## 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} =} ctmcfpt (@var{Q})
+## @deftypefnx {Function File} {@var{m} =} ctmcfpt (@var{Q}, @var{i}, @var{j})
+##
+## @cindex first passage times, CTMC
+## @cindex CTMC
+## @cindex continuous time Markov chain
+## @cindex Markov chain, continuous time
+##
+## Compute mean first passage times for an irreducible continuous-time
+## Markov chain.
+##
+## @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.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item M
+## @code{@var{M}(i,j)} is the average time before state
+## @var{j} is visited for the first time, starting from state @var{i}.
+## We set @code{@var{M}(i,i) = 0}.
+##
+## @item m
+## @var{m} is the average time before state @var{j} is visited for the first 
+## time, starting from state @var{i}.
+##
+## @end table
+##
+## @seealso{dtmcfpt}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function result = ctmcfpt( Q, i, j )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin != 1 && nargin != 3 )
+    print_usage();
+  endif
+
+  [N err] = ctmcchkQ(Q);
+  
+  (N>0) || ...
+      error(err);
+
+  if ( nargin == 1 ) 
+    result = 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;
+	result(i,j) = ctmcmtta(QQ,p0);
+      endfor
+    endfor
+  else
+    (isscalar(i) && i>=1 && j<=N) || error("i must be an integer in the range 1..%d", N);
+    (isvector(j) && all(j>=1) && all(j<=N)) || error("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 = ctmcmtta(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 = ctmcfpt(Q)
+%! m = ctmcfpt(Q,1,3)
+
+%!test
+%! N = 10;
+%! Q = reshape(1:N^2,N,N);
+%! Q(1:N+1:end) = 0;
+%! Q -= diag(sum(Q,2));
+%! M = ctmcfpt(Q);
+%! assert( all(diag(M) < 10*eps) );
diff --git a/inst/ctmcmtta.m b/inst/ctmcmtta.m
new file mode 100644
index 0000000..61676e0
--- /dev/null
+++ b/inst/ctmcmtta.m
@@ -0,0 +1,134 @@
+## 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} =} ctmcmtta (@var{Q}, @var{p})
+##
+## @cindex Markov chain, continuous time
+## @cindex continuous time Markov chain
+## @cindex CTMC
+## @cindex mean time to absorption, CTMC
+##
+## Compute the Mean-Time to Absorption (MTTA) of the CTMC described by
+## the infinitesimal generator matrix @var{Q}, starting from initial
+## occupancy probabilities @var{p}. 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
+##
+## @seealso{dtmcmtta}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function t = ctmcmtta( Q, p )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin != 2 )
+    print_usage();
+  endif
+
+  [N err] = ctmcchkQ(Q);
+
+  (N>0) || ...
+      error(err);
+
+  ( isvector(p) && length(p) == N && all(p>=0) && abs(sum(p)-1.0)<epsilon ) || ...
+      error( "p must be a probability vector" );
+  p = p(:)';
+
+  L = ctmcexps(Q,p);
+  t = sum(L);
+endfunction
+%!test
+%! Q = [0 1 0; 1 0 1; 0 1 0 ]; Q -= diag( sum(Q,2) );
+%! fail( "ctmcmtta(Q,[1 0 0])", "no absorbing");
+
+%!test
+%! Q = [0 1 0; 1 0 1; 0 0 0; 0 0 0 ];
+%! fail( "ctmcmtta(Q,[1 0 0])", "square matrix");
+
+%!test
+%! Q = [0 1 0; 1 0 1; 0 0 0 ];
+%! fail( "ctmcmtta(Q,[1 0 0])", "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( ctmcmtta( Q,[0 0 0 1] ), 0 ); # state 4 is absorbing
+
+%!test
+%! Q = [-1 1; 0 0];
+%! assert( ctmcmtta( Q, [0 1] ), 0 ); # state 2 is absorbing
+%! assert( ctmcmtta( 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;
+%! birth = 0*death;
+%! Q = ctmcbd(birth,death);
+%! t = ctmcmtta(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) = ctmcmtta(Q,p);
+%! endfor
+%! plot(initial_state,t,"+");
+%! xlabel("Initial state");
+%! ylabel("MTTA");
+
+
diff --git a/inst/ctmctaexps.m b/inst/ctmctaexps.m
new file mode 100644
index 0000000..47091c5
--- /dev/null
+++ b/inst/ctmctaexps.m
@@ -0,0 +1,153 @@
+## 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} =} ctmctaexps (@var{Q}, @var{t}, @var{p})
+## @deftypefnx {Function File} {@var{M} =} ctmctaexps (@var{Q}, @var{p})
+##
+## @cindex Markov chain, continuous time
+## @cindex time-alveraged sojourn time, CTMC
+## @cindex continuous time Markov chain
+## @cindex CTMC
+##
+## Compute the @emph{time-averaged sojourn time} @code{@var{M}(i)},
+## defined as the fraction of the time interval @math{[0,t]} (or until
+## absorption) spent in state @math{i}, assuming that the state
+## occupancy probabilities at time 0 are @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 @math{\sum_{j=1}^N Q_{ij} = 0}
+##
+## @item t
+## Time. If omitted, the results are computed until absorption.
+##
+## @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
+## When called with three arguments, @code{@var{M}(i)} is the expected
+## fraction of the interval @math{[0,t]} spent in state @math{i}
+## assuming that the state occupancy probability at time zero is
+## @var{p}. When called with two arguments, @code{@var{M}(i)} is the
+## expected fraction of time until absorption spent in state @math{i};
+## in this case the mean time to absorption is @code{sum(@var{M})}.
+##
+## @end table
+##
+## @seealso{dtmctaexps}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function M = ctmctaexps( Q, varargin )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+
+  L = ctmcexps(Q,varargin{:});
+  M = L ./ repmat(sum(L,2),1,columns(L));
+endfunction
+%!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) );
+%! M = ctmctaexps(Q, [1 0 0 0]);
+%! assert( sum(M), 1, 10*eps );
+
+%!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-5,30,100);
+%! p = zeros(1,N); p(1)=1;
+%! M = zeros(length(t),N);
+%! for i=1:length(t)
+%!   M(i,:) = ctmctaexps(Q,t(i),p);
+%! endfor
+%! clf;
+%! 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(0,1*day,101);
+%! PP = ctmctaexps(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
+%! clf;
+%! semilogy(TT,A,";Steady-state;", ...
+%!      TT,At,";Instantaneous;", ...
+%!      TT,Abart,";Interval base;");
+%! ax = axis();
+%! ax(3) = 1-1e-5;
+%! axis(ax);
diff --git a/inst/dtmc.m b/inst/dtmc.m
new file mode 100644
index 0000000..50c35e5
--- /dev/null
+++ b/inst/dtmc.m
@@ -0,0 +1,189 @@
+## 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 DTMC
+## @cindex Markov chain, stationary probabilities
+## @cindex Markov chain, transient probabilities
+##
+## Compute stationary or transient state occupancy probabilities for a discrete-time Markov chain.
+##
+## With a single argument, compute the stationary state occupancy
+## probability vector @code{@var{p}(1), @dots{}, @var{p}(N)} for a
+## discrete-time Markov chain with state space @math{@{1, 2, @dots{},
+## N@}} and with @math{N \times N} transition probability matrix
+## @var{P}. With three arguments, compute the transient state occupancy
+## vector @code{@var{p}(1), @dots{}, @var{p}(N)} that the system is in
+## state @math{i} after @var{n} steps, given initial occupancy
+## probabilities @var{p0}(1), @dots{}, @var{p0}(N).
+##
+## @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
+## Number of transitions after which compute the state occupancy probabilities
+## (@math{n=0, 1, @dots{}})
+##
+## @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 called with a single argument, @code{@var{p}(i)}
+## is the steady-state probability that the system is in state @math{i}.
+## If this function is called with three arguments, @code{@var{p}(i)}
+## is the probability that the system is in state @math{i}
+## after @var{n} transitions, given the initial probabilities
+## @code{@var{p0}(i)} that the initial state is @math{i}.
+##
+## @end table
+##
+## @seealso{ctmc}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function p = dtmc( P, n, p0 )
+
+  if ( nargin != 1 && nargin != 3 )
+    print_usage();
+  endif
+
+  [N err] = dtmcchkP(P);
+  
+  ( N>0 ) || ...
+      error( err );
+
+  if ( nargin == 1 ) # steady-state analysis
+    A = P-eye(N);
+    A(:,N) = 1; # add normalization condition
+    rank( A ) == N || ...
+	warning( "dtmc(): P is reducible" );
+    
+    b = [ zeros(1,N-1) 1 ];
+    p = b/A;
+  else # transient analysis
+    ( isscalar(n) && n>=0 ) || ...
+	error( "n must be >=0" );
+
+    ( isvector(p0) && length(p0) == N && all(p0>=0) && abs(sum(p0)-1.0)<N*eps ) || ...
+        error( "p0 must be a probability vector" );   
+
+    p0 = p0(:)'; # make p0 a row vector
+
+    p = p0*P^n;
+  endif
+endfunction
+
+%!test
+%! P = [0.75 0.25; 0.5 0.5];
+%! p = dtmc(P);
+%! assert( p*P, p, 1e-5 );
+%! assert( p, [0.6666 0.3333], 1e-4 );
+
+%!test
+%! #Example 2.11 p. 44 Bolch et al.
+%! P = [0.5 0.5; 0.5 0.5];
+%! p = dtmc(P);
+%! assert( p, [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 );
+
+%!test
+%! P = [0 1 0 0 0; ...
+%!      .25 0 .75 0 0; ...
+%!      0 .5 0 .5 0; ...
+%!      0 0 .75 0 .25; ...
+%!      0 0 0 1 0 ];
+%! p = dtmc(P);
+%! assert( p, [.0625 .25 .375 .25 .0625], 10*eps );
+
+## "Rat maze" problem (p. 441 of [GrSn97]);
+%!test
+%! P = zeros(9,9);
+%! P(1,[2 4]) = 1/2;
+%! P(2,[1 5 3]) = 1/3;
+%! P(3,[2 6]) = 1/2;
+%! P(4,[1 5 7]) = 1/3;
+%! P(5,[2 4 6 8]) = 1/4;
+%! P(6,[3 5 9]) = 1/3;
+%! P(7,[4 8]) = 1/2;
+%! P(8,[7 5 9]) = 1/3;
+%! P(9,[6 8]) = 1/2;
+%! p = dtmc(P);
+%! assert( p, [1/12 1/8 1/12 1/8 1/6 1/8 1/12 1/8 1/12], 10*eps );
+
+%!demo
+%! P = zeros(9,9);
+%! P(1,[2 4]    ) = 1/2;
+%! P(2,[1 5 3]  ) = 1/3;
+%! P(3,[2 6]    ) = 1/2;
+%! P(4,[1 5 7]  ) = 1/3;
+%! P(5,[2 4 6 8]) = 1/4;
+%! P(6,[3 5 9]  ) = 1/3;
+%! P(7,[4 8]    ) = 1/2;
+%! P(8,[7 5 9]  ) = 1/3;
+%! P(9,[6 8]    ) = 1/2;
+%! p = dtmc(P);
+%! disp(p)
+
+%!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");
diff --git a/inst/dtmc_bd.m b/inst/dtmc_bd.m
new file mode 100644
index 0000000..8a8d5ba
--- /dev/null
+++ b/inst/dtmc_bd.m
@@ -0,0 +1,43 @@
+## Copyright (C) 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_bd (@var{b}, @var{d})
+##
+## This function is deprecated. Please use @code{dtmcbd} instead.
+##
+## @seealso{dtmcbd}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function P = dtmc_bd( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "dtmc_bd is deprecated. Please use dtmcbd instead");
+  endif
+  P = dtmcbd( varargin{:} );
+endfunction
+%!test
+%! birth = [.5 .5 .3];
+%! death = [.6 .2 .3];
+%! fail("dtmc_bd(birth,death)","must be");
diff --git a/inst/dtmc_check_P.m b/inst/dtmc_check_P.m
new file mode 100644
index 0000000..696ccf0
--- /dev/null
+++ b/inst/dtmc_check_P.m
@@ -0,0 +1,63 @@
+## 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{r} @var{err}] =} dtmc_check_P (@var{P})
+##
+## This function is deprecated. Please use @code{dtmcchkP} instead.
+##
+## @seealso{dtmcchkP}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [result err] = dtmc_check_P( P )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "dtmc_check_P is deprecated. Please use dtmcchkP instead");
+  endif
+  [result err] = dtmcchkP( P );
+endfunction
+%!test
+%! [r err] = dtmc_check_P( [1 1 1; 1 1 1] );
+%! assert( r, 0 );
+%! assert( index(err, "square") > 0 );
+
+%!test
+%! [r err] = dtmc_check_P( [1 0 0; 0 0.5 0; 0 0 0] );
+%! assert( r, 0 );
+%! assert( index(err, "stochastic") > 0 );
+
+%!test
+%! P = [0 1; 1 0];
+%! assert( dtmc_check_P(P), 2 );
+
+%!test
+%! P = dtmc_bd( linspace(0.1,0.4,10), linspace(0.4,0.1,10) );
+%! assert( dtmc_check_P(P), rows(P) );
+
+%!test
+%! N = 1000;
+%! P = reshape( 1:N^2, N, N );
+%! P(1:N+1:end) = 0;
+%! P = P ./ repmat(sum(P,2),1,N);
+%! assert( dtmc_check_P(P), N );
\ No newline at end of file
diff --git a/inst/dtmc_exps.m b/inst/dtmc_exps.m
new file mode 100644
index 0000000..e9fbbff
--- /dev/null
+++ b/inst/dtmc_exps.m
@@ -0,0 +1,52 @@
+## Copyright (C) 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} =} dtmc_exps (@var{P}, @var{n}, @var{p0})
+## @deftypefnx {Function File} {@var{L} =} dtmc_exps (@var{P}, @var{p0})
+##
+## This function is deprecated. Please use @code{dtmcexps} instead.
+##
+## @seealso{dtmcexps}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function L = dtmc_exps ( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "dtmc_exps is deprecated. Please use dtmcexps instead");
+  endif
+  L = dtmcexps( varargin{:} );
+endfunction
+%!test
+%! P = dtmc_bd([1 1 1 1], [0 0 0 0]);
+%! L = dtmc_exps(P,[1 0 0 0 0]);
+%! t = dtmc_mtta(P,[1 0 0 0 0]);
+%! assert( L, [1 1 1 1 0] );
+%! assert( sum(L), t );
+
+%!test
+%! P = dtmc_bd(linspace(0.1,0.4,5),linspace(0.4,0.1,5));
+%! p0 = [1 0 0 0 0 0];
+%! L = dtmc_exps(P,0,p0);
+%! assert( L, p0 );
\ No newline at end of file
diff --git a/inst/dtmc_fpt.m b/inst/dtmc_fpt.m
new file mode 100644
index 0000000..b2aeef3
--- /dev/null
+++ b/inst/dtmc_fpt.m
@@ -0,0 +1,96 @@
+## 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})
+##
+## This function is deprecated. Please use @code{dtmcfpt} instead.
+##
+## @seealso{ctmcfpt}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function result = dtmc_fpt( P )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "dtmc_fpt is deprecated. Please use dtmcfpt instead");
+  endif
+  result = dtmcfpt( P );
+endfunction
+%!test
+%! P = [1 1 1; 1 1 1];
+%! fail( "dtmc_fpt(P)" );
+
+%!test
+%! P = dtmc_bd([1 1 1], [0 0 0] );
+%! fail( "dtmc_fpt(P)", "absorbing" );
+
+%!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 );
+
+## Example on p. 461 of [GrSn97]
+%!test
+%! P = [ 0 1 0 0 0; ...
+%!      .25 .0 .75 0 0; ...
+%!      0 .5 0 .5 0; ...
+%!      0 0 .75 0 .25; ...
+%!      0 0 0 1 0 ];
+%! M = dtmc_fpt(P);
+%! assert( M, [16 1 2.6667 6.3333 21.3333; ...
+%!             15 4 1.6667 5.3333 20.3333; ...
+%!             18.6667 3.6667 2.6667 3.6667 18.6667; ...
+%!             20.3333 5.3333 1.6667 4 15; ...
+%!             21.3333 6.3333 2.6667 1 16 ], 1e-4 );
+
+%!test
+%! sz = 10;
+%! P = reshape( 1:sz^2, sz, sz );
+%! 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);
+%!   endfor
+%! endfor
+
+## "Rat maze" problem (p. 453 of [GrSn97]);
+%!test
+%! P = zeros(9,9);
+%! P(1,[2 4]) = .5;
+%! P(2,[1 5 3]) = 1/3;
+%! P(3,[2 6]) = .5;
+%! P(4,[1 5 7]) = 1/3;
+%! P(5,[2 4 6 8]) = 1/4;
+%! P(6,[3 5 9]) = 1/3;
+%! P(7,[4 8]) = .5;
+%! P(8,[7 5 9]) = 1/3;
+%! P(9,[6 8]) = .5;
+%! M = dtmc_fpt(P);
+%! assert( M(1:9 != 5,5)', [6 5 6 5 5 6 5 6], 100*eps );
+
diff --git a/inst/dtmc_is_irreducible.m b/inst/dtmc_is_irreducible.m
new file mode 100644
index 0000000..afcec0e
--- /dev/null
+++ b/inst/dtmc_is_irreducible.m
@@ -0,0 +1,97 @@
+## Copyright (C) 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{r} @var{s}] =} dtmc_is_irreducible (@var{P})
+##
+## This function is deprecated. Please use @code{dtmcisir} instead.
+## 
+## @seealso{dtmcisir}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [r s] = dtmc_is_irreducible( P )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "dtmc_is_irreducible is deprecated. Please use dtmcisir instead");
+  endif
+  [r s] = dtmcisir( P );
+endfunction
+%!test
+%! P = [0 .5 0; 0 0 0];
+%! fail( "dtmc_is_irresudible(P)" );
+
+%!test
+%! P = [0 1 0; 0 .5 .5; 0 1 0];
+%! [r s] = dtmc_is_irreducible(P);
+%! assert( r == 0 );
+%! assert( max(s), 2 );
+%! assert( min(s), 1 );
+
+%!test
+%! P = [.5 .5 0; .2 .3 .5; 0 .2 .8];
+%! [r s] = dtmc_is_irreducible(P);
+%! assert( r == 1 );
+%! assert( max(s), 1 );
+%! assert( min(s), 1 );
+
+## FIXME: (mosty) copied from qnvisits.m; use a better algorithm for SCC
+## (e.g.,
+## http://pmtksupport.googlecode.com/svn/trunk/gaimc1.0-graphAlgo/scomponents.m
+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
+
+## FIXME: (mosty) copied from qnvisits.m
+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; v(s) = 1;
+  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
diff --git a/inst/dtmc_mtta.m b/inst/dtmc_mtta.m
new file mode 100644
index 0000000..4659671
--- /dev/null
+++ b/inst/dtmc_mtta.m
@@ -0,0 +1,82 @@
+## Copyright (C) 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{N} @var{B}] =} dtmc_mtta (@var{P})
+## @deftypefnx {Function File} {[@var{t} @var{N} @var{B}] =} dtmc_mtta (@var{P}, @var{p0})
+##
+## This function is deprecated. Please use @code{dtmcmtta} instead.
+##
+## @seealso{ctmcmtta}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [t N B] = dtmc_mtta( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function", 
+	    "dtmc_mtta is deprecated. Please use dtmcmtta instead");
+  endif
+  [t N B] = dtmcmtta( varargin{:} );
+endfunction
+%!test
+%! fail( "dtmc_mtta(1,2,3)" );
+%! fail( "dtmc_mtta()" );
+
+%!test
+%! P = dtmcbd([0 .5 .5 .5], [.5 .5 .5 0]);
+%! [t N B] = dtmc_mtta(P);
+%! assert( t, [0 3 4 3 0], 10*eps );
+%! assert( B([2 3 4],[1 5]), [3/4 1/4; 1/2 1/2; 1/4 3/4], 10*eps );
+%! assert( B(1,1), 1 );
+%! assert( B(5,5), 1 );
+
+%!test
+%! P = dtmcbd([0 .5 .5 .5], [.5 .5 .5 0]);
+%! [t N B] = dtmc_mtta(P);
+%! assert( t(3), 4, 10*eps );
+%! assert( B(3,1), 0.5, 10*eps );
+%! assert( B(3,5), 0.5, 10*eps );
+
+## Example on p. 422 of [GrSn97]
+%!test
+%! P = dtmcbd([0 .5 .5 .5 .5], [.5 .5 .5 .5 0]);
+%! [t N B] = dtmc_mtta(P);
+%! assert( t(2:5), [4 6 6 4], 100*eps );
+%! assert( B(2:5,1), [.8 .6 .4 .2]', 100*eps );
+%! assert( B(2:5,6), [.2 .4 .6 .8]', 100*eps );
+
+## "Rat maze" problem (p. 453 of [GrSn97]);
+%!test
+%! P = zeros(9,9);
+%! P(1,[2 4]) = .5;
+%! P(2,[1 5 3]) = 1/3;
+%! P(3,[2 6]) = .5;
+%! P(4,[1 5 7]) = 1/3;
+%! P(5,:) = 0; P(5,5) = 1;
+%! P(6,[3 5 9]) = 1/3;
+%! P(7,[4 8]) = .5;
+%! P(8,[7 5 9]) = 1/3;
+%! P(9,[6 8]) = .5;
+%! t = dtmc_mtta(P);
+%! assert( t, [6 5 6 5 0 5 6 5 6], 10*eps );
+
diff --git a/inst/dtmc_taexps.m b/inst/dtmc_taexps.m
new file mode 100644
index 0000000..43f4b09
--- /dev/null
+++ b/inst/dtmc_taexps.m
@@ -0,0 +1,46 @@
+## Copyright (C) 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} =} dtmc_taexps (@var{P}, @var{n}, @var{p0})
+## @deftypefnx {Function File} {@var{L} =} dtmc_taexps (@var{P}, @var{p0})
+##
+## This function is deprecated. Please use @code{dtmctaexps} instead.
+##
+## @code{dtmcexps}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function M = dtmc_taexps( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "dtmc_taexps is deprecated. Please use dtmctaexps instead");
+  endif
+  M = dtmctaexps( varargin{:} );
+endfunction
+%!test
+%! P = dtmc_bd([1 1 1 1], [0 0 0 0]);
+%! p0 = [1 0 0 0 0];
+%! L = dtmc_taexps(P,p0);
+%! assert( L, [.25 .25 .25 .25 0], 10*eps );
+
diff --git a/inst/dtmcbd.m b/inst/dtmcbd.m
new file mode 100644
index 0000000..63e3227
--- /dev/null
+++ b/inst/dtmcbd.m
@@ -0,0 +1,106 @@
+## Copyright (C) 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} =} dtmcbd (@var{b}, @var{d})
+##
+## @cindex Markov chain, discrete time
+## @cindex DTMC
+## @cindex discrete time Markov chain
+## @cindex birth-death process, DTMC
+##
+## Returns the transition probability matrix @math{P} for a discrete
+## birth-death process over state space @math{1, 2, @dots{}, N}.
+## @code{@var{b}(i)} is the transition probability from state
+## @math{i} to @math{i+1}, and @code{@var{d}(i)} is the transition
+## probability from state @math{i+1} to state @math{i}, @math{i=1, 2,
+## @dots{}, N-1}.
+##
+## Matrix @math{\bf P} is therefore defined as:
+##
+## @tex
+## $$ \pmatrix{ (1-\lambda_1) & \lambda_1 & & & & \cr
+##              \mu_1 & (1 - \mu_1 - \lambda_2) & \lambda_2 & & \cr
+##              & \mu_2 & (1 - \mu_2 - \lambda_3) & \lambda_3 & & \cr
+##              \cr
+##              & & \ddots & \ddots & \ddots & & \cr
+##              \cr
+##              & & & \mu_{N-2} & (1 - \mu_{N-2}-\lambda_{N-1}) & \lambda_{N-1} \cr
+##              & & & & \mu_{N-1} & (1-\mu_{N-1}) }
+## $$
+## @end tex
+## @ifnottex
+## @example
+## @group
+## /                                                             \
+## | 1-b(1)     b(1)                                             |
+## |  d(1)  (1-d(1)-b(2))     b(2)                               |
+## |            d(2)      (1-d(2)-b(3))     b(3)                 |
+## |                                                             |
+## |                 ...           ...          ...              |
+## |                                                             |
+## |                         d(N-2)   (1-d(N-2)-b(N-1))  b(N-1)  |
+## |                                        d(N-1)      1-d(N-1) |
+## \                                                             /
+## @end group
+## @end example
+## @end ifnottex
+##
+## @noindent where @math{\lambda_i} and @math{\mu_i} are the birth and
+## death probabilities, respectively.
+##
+## @seealso{ctmcbd}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function P = dtmcbd( b, d )
+
+  if ( nargin != 2 ) 
+    print_usage();
+  endif
+
+  ( isvector( b ) && isvector( d ) ) || ...
+      error( "birth and death must be vectors" );
+  b = b(:); # make b a column vector
+  d = d(:); # make d a column vector
+  size_equal( b, d ) || ...
+      error( "birth and death vectors must have the same length" );
+  all( b >= 0 ) || ...
+      error( "birth probabilities must be >= 0" );
+  all( d >= 0 ) || ...
+      error( "death probabilities must be >= 0" );
+  all( ([b; 0] + [0; d]) <= 1 ) || ...
+      error( "d(i)+b(i+1) must be <= 1");
+
+  P = diag( b, 1 ) + diag( d, -1 );
+  P += diag( 1-sum(P,2) );
+endfunction
+%!test
+%! birth = [.5 .5 .3];
+%! death = [.6 .2 .3];
+%! fail("dtmcbd(birth,death)","must be");
+
+%!demo
+%! birth = [ .2 .3 .4 ];
+%! death = [ .1 .2 .3 ];
+%! P = dtmcbd( birth, death );
+%! disp(P)
+
diff --git a/inst/dtmcchkP.m b/inst/dtmcchkP.m
new file mode 100644
index 0000000..567294b
--- /dev/null
+++ b/inst/dtmcchkP.m
@@ -0,0 +1,81 @@
+## 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{r} @var{err}] =} dtmcchkP (@var{P})
+##
+## @cindex Markov chain, discrete time
+## @cindex DTMC
+## @cindex discrete time Markov chain
+##
+## Check whether @var{P} is a valid transition probability matrix. 
+##
+## If @var{P} is valid, @var{r} is the size (number of rows or columns)
+## of @var{P}. If @var{P} is not a transition probability matrix,
+## @var{r} is set to zero, and @var{err} to an appropriate error string.
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [result err] = dtmcchkP( P )
+
+  if ( nargin != 1 )
+    print_usage();
+  endif
+
+  result = 0;
+  err = "";
+
+  if ( !issquare(P) )
+    err = "P is not a square matrix";
+    return;
+  endif
+  
+  if (  any(P(:)<-eps) || norm( sum(P,2) - 1, "inf" ) > columns(P)*eps )
+    err = "P is not a stochastic matrix";
+    return;
+  endif
+
+  result = rows(P);
+endfunction
+%!test
+%! [r err] = dtmcchkP( [1 1 1; 1 1 1] );
+%! assert( r, 0 );
+%! assert( index(err, "square") > 0 );
+
+%!test
+%! [r err] = dtmcchkP( [1 0 0; 0 0.5 0; 0 0 0] );
+%! assert( r, 0 );
+%! assert( index(err, "stochastic") > 0 );
+
+%!test
+%! P = [0 1; 1 0];
+%! assert( dtmcchkP(P), 2 );
+
+%!test
+%! P = dtmcbd( linspace(0.1,0.4,10), linspace(0.4,0.1,10) );
+%! assert( dtmcchkP(P), rows(P) );
+
+%!test
+%! N = 1000;
+%! P = reshape( 1:N^2, N, N );
+%! P(1:N+1:end) = 0;
+%! P = P ./ repmat(sum(P,2),1,N);
+%! assert( dtmcchkP(P), N );
\ No newline at end of file
diff --git a/inst/dtmcexps.m b/inst/dtmcexps.m
new file mode 100644
index 0000000..b5fc172
--- /dev/null
+++ b/inst/dtmcexps.m
@@ -0,0 +1,148 @@
+## Copyright (C) 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} =} dtmcexps (@var{P}, @var{n}, @var{p0})
+## @deftypefnx {Function File} {@var{L} =} dtmcexps (@var{P}, @var{p0})
+##
+## @cindex expected sojourn times, DTMC
+## @cindex DTMC
+## @cindex discrete time Markov chain
+## @cindex Markov chain, discrete time
+##
+## Compute the expected number of visits to each state during the first
+## @var{n} transitions, or until abrosption.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @math{N \times N} transition probability matrix.
+##
+## @item n
+## Number of steps during which the expected number of visits are
+## computed (@math{@var{n} @geq{} 0}). If @code{@var{n}=0}, returns
+## @var{p0}. If @code{@var{n} > 0}, returns the expected number of
+## visits after exactly @var{n} transitions.
+##
+## @item p0
+## Initial state occupancy probability.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item L
+## When called with two arguments, @code{@var{L}(i)} is the expected
+## number of visits to transient state @math{i} before absorption. When
+## called with three arguments, @code{@var{L}(i)} is the expected number
+## of visits to state @math{i} during the first @var{n} transitions,
+## given initial occupancy probability @var{p0}.
+##
+## @end table
+##
+## @seealso{ctmcexps}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function L = dtmcexps ( P, varargin )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+
+  [K err] = dtmcchkP(P);
+
+  (K>0) || ...
+      error(err);
+
+  if ( nargin == 2 )
+    p0 = varargin{1};
+  else
+    n = varargin{1};
+    p0 = varargin{2};
+  endif
+
+  ( isvector(p0) && length(p0) == K && all(p0>=0) && abs(sum(p0)-1.0)<epsilon ) || ...
+      error( "p0 must be a state occupancy probability vector" );
+
+  p0 = p0(:)'; # make p0 a row vector
+
+  if ( nargin == 3 )
+    isscalar(n) && n>=0 || ...
+	error("n must be >=0");
+    n = fix(n);
+    L = zeros(sizeof(p0));
+
+    ## It is know that 
+    ##
+    ## I + P + P^2 + P^3 + ... + P^n = (I-P)^-1 * (I-P^(n+1))
+    ##
+    ## and therefore we could succintly write
+    ##
+    ## L = p0*inv(eye(K)-P)*(eye(K)-P^(n+1));
+    ##
+    ## Unfortunatly, the method above is numerically unstable (at least
+    ## for small values of n), so we use the crude approach below.
+
+    PP = p0;
+    L = zeros(1,K);
+    for p=0:n
+      L += PP;
+      PP *= P;
+    endfor
+  else
+    ## identify transient states
+    tr = find(diag(P) < 1);
+    k = length(tr); # number of transient states
+    if ( k == K )
+      error("There are no absorbing states");
+    endif
+    
+    N = zeros(size(P));
+    
+    ## Source: Grinstead, Charles M.; Snell, J. Laurie (July 1997). "Ch.
+    ## 11: Markov Chains". Introduction to Probability. American
+    ## Mathematical Society. ISBN 978-0821807491.
+    
+    ## http://www.cs.virginia.edu/~gfx/Courses/2006/DataDriven/bib/texsyn/Chapter11.pdf
+    tmpN = inv(eye(k) - P(tr,tr)); # matrix N = (I-Q)^-1
+    N(tr,tr) = tmpN;
+    L = p0*N;
+  endif
+endfunction
+%!test
+%! P = dtmcbd([1 1 1 1], [0 0 0 0]);
+%! L = dtmcexps(P,[1 0 0 0 0]);
+%! t = dtmcmtta(P,[1 0 0 0 0]);
+%! assert( L, [1 1 1 1 0] );
+%! assert( sum(L), t );
+
+%!test
+%! P = dtmcbd(linspace(0.1,0.4,5),linspace(0.4,0.1,5));
+%! p0 = [1 0 0 0 0 0];
+%! L = dtmcexps(P,0,p0);
+%! assert( L, p0 );
\ No newline at end of file
diff --git a/inst/dtmcfpt.m b/inst/dtmcfpt.m
new file mode 100644
index 0000000..9e75293
--- /dev/null
+++ b/inst/dtmcfpt.m
@@ -0,0 +1,145 @@
+## 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} =} dtmcfpt (@var{P})
+##
+## @cindex first passage times
+## @cindex mean recurrence times
+## @cindex discrete time Markov chain
+## @cindex Markov chain, discrete time
+## @cindex DTMC
+##
+## Compute mean first passage times and mean recurrence times
+## for an irreducible discrete-time Markov chain.
+##
+## @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.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item M
+## For all @math{i \neq j}, @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}. @code{@var{M}(i,i)} is the @emph{mean
+## recurrence time} of state @math{i}, and represents the average time
+## needed to return to state @var{i}.
+##
+## @end table
+##
+## @seealso{ctmcfpt}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function result = dtmcfpt( P )
+
+  if ( nargin != 1 )
+    print_usage();
+  endif
+
+  [N err] = dtmcchkP(P);
+
+  ( N>0 ) || ...
+      error(err);
+
+  if ( any(diag(P) == 1) ) 
+    error("Cannot compute first passage times for absorbing chains");
+  endif
+
+  ## Source [GrSn97]
+  w = dtmc(P); # steady state probability vector
+  W = repmat(w,N,1);
+  ## Z = (I - P + W)^-1 where W is the matrix where each row is the
+  ## steady-state probability vector for P
+  Z = inv(eye(N)-P+W);
+  ## m_ij = (z_jj - z_ij) / w_j
+  result = (repmat(diag(Z)',N,1) - Z) ./ repmat(w,N,1) + diag(1./w);
+
+endfunction
+%!test
+%! P = [1 1 1; 1 1 1];
+%! fail( "dtmcfpt(P)" );
+
+%!test
+%! P = dtmcbd([1 1 1], [0 0 0] );
+%! fail( "dtmcfpt(P)", "absorbing" );
+
+%!test
+%! P = [ 0.0 0.9 0.1; ...
+%!       0.1 0.0 0.9; ...
+%!       0.9 0.1 0.0 ];
+%! p = dtmc(P);
+%! M = dtmcfpt(P);
+%! assert( diag(M)', 1./p, 1e-8 );
+
+## Example on p. 461 of [GrSn97]
+%!test
+%! P = [ 0 1 0 0 0; ...
+%!      .25 .0 .75 0 0; ...
+%!      0 .5 0 .5 0; ...
+%!      0 0 .75 0 .25; ...
+%!      0 0 0 1 0 ];
+%! M = dtmcfpt(P);
+%! assert( M, [16 1 2.6667 6.3333 21.3333; ...
+%!             15 4 1.6667 5.3333 20.3333; ...
+%!             18.6667 3.6667 2.6667 3.6667 18.6667; ...
+%!             20.3333 5.3333 1.6667 4 15; ...
+%!             21.3333 6.3333 2.6667 1 16 ], 1e-4 );
+
+%!test
+%! sz = 10;
+%! P = reshape( 1:sz^2, sz, sz );
+%! normP = repmat(sum(P,2),1,columns(P));
+%! P = P./normP;
+%! M = dtmcfpt(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);
+%!   endfor
+%! endfor
+
+## "Rat maze" problem (p. 453 of [GrSn97]);
+%!test
+%! P = zeros(9,9);
+%! P(1,[2 4]) = .5;
+%! P(2,[1 5 3]) = 1/3;
+%! P(3,[2 6]) = .5;
+%! P(4,[1 5 7]) = 1/3;
+%! P(5,[2 4 6 8]) = 1/4;
+%! P(6,[3 5 9]) = 1/3;
+%! P(7,[4 8]) = .5;
+%! P(8,[7 5 9]) = 1/3;
+%! P(9,[6 8]) = .5;
+%! M = dtmcfpt(P);
+%! assert( M(1:9 != 5,5)', [6 5 6 5 5 6 5 6], 100*eps );
+
diff --git a/inst/dtmcisir.m b/inst/dtmcisir.m
new file mode 100644
index 0000000..95f77ee
--- /dev/null
+++ b/inst/dtmcisir.m
@@ -0,0 +1,133 @@
+## Copyright (C) 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{r} @var{s}] =} dtmcisir (@var{P})
+##
+## @cindex Markov chain, discrete time
+## @cindex discrete time Markov chain
+## @cindex DTMC
+## @cindex irreducible Markov chain
+##
+## Check if @var{P} is irreducible, and identify Strongly Connected
+## Components (SCC) in the transition graph of the DTMC with transition
+## probability matrix @var{P}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @code{@var{P}(i,j)} is the transition probability from state @math{i}
+## to state @math{j}. This function does not currently check whether
+## @var{P} is a valid transition probability matrix.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item r
+## 1 if @var{P} irreducible, 0 otherwise.
+##
+## @item s
+## @code{@var{s}(i)} is the SCC that state @math{i} belongs to. SCCs are
+## numbered as 1, 2, @dots{}. If the graph is strongly connected, then
+## there is a single SCC and the predicate @code{all(s == 1)} evaluates
+## to true.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [r s] = dtmcisir( P )
+
+  if ( nargin != 1 )
+    print_usage();
+  endif
+
+  [N err] = dtmcchkP(P);
+  if ( N == 0 ) 
+    error(err);
+  endif
+  s = __scc(P);
+  r = (max(s) == 1);
+
+endfunction
+%!test
+%! P = [0 .5 0; 0 0 0];
+%! fail( "dtmcisir(P)" );
+
+%!test
+%! P = [0 1 0; 0 .5 .5; 0 1 0];
+%! [r s] = dtmcisir(P);
+%! assert( r == 0 );
+%! assert( max(s), 2 );
+%! assert( min(s), 1 );
+
+%!test
+%! P = [.5 .5 0; .2 .3 .5; 0 .2 .8];
+%! [r s] = dtmcisir(P);
+%! assert( r == 1 );
+%! assert( max(s), 1 );
+%! assert( min(s), 1 );
+
+## FIXME: (mosty) copied from qncmvisits.m; use a better algorithm for SCC
+## (e.g.,
+## http://pmtksupport.googlecode.com/svn/trunk/gaimc1.0-graphAlgo/scomponents.m
+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
+
+## FIXME: (mosty) copied from qncmvisits.m
+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; v(s) = 1;
+  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
diff --git a/inst/dtmcmtta.m b/inst/dtmcmtta.m
new file mode 100644
index 0000000..d8676ab
--- /dev/null
+++ b/inst/dtmcmtta.m
@@ -0,0 +1,250 @@
+## Copyright (C) 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{N} @var{B}] =} dtmcmtta (@var{P})
+## @deftypefnx {Function File} {[@var{t} @var{N} @var{B}] =} dtmcmtta (@var{P}, @var{p0})
+##
+## @cindex mean time to absorption, DTMC
+## @cindex absorption probabilities, DTMC
+## @cindex fundamental matrix
+## @cindex DTMC
+## @cindex discrete time Markov chain
+## @cindex Markov chain, discrete time
+##
+## Compute the expected number of steps before absorption for a
+## DTMC with @math{N \times N} transition probability matrix @var{P};
+## compute also the fundamental matrix @var{N} for @var{P}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @math{N \times N} transition probability matrix.
+##
+## @end table
+##
+## @strong{OUTPUTS} 
+##
+## @table @var
+##
+## @item t
+## When called with a single argument, @var{t} is a vector of size
+## @math{N} such that @code{@var{t}(i)} is the expected number of steps
+## before being absorbed in any absorbing state, starting from state
+## @math{i}; if @math{i} is absorbing, @code{@var{t}(i) = 0}. When
+## called with two arguments, @var{t} is a scalar, and represents the
+## expected number of steps before absorption, starting from the initial
+## state occupancy probability @var{p0}.
+##
+## @item N
+## When called with a single argument, @var{N} is the @math{N \times N}
+## fundamental matrix for @var{P}. @code{@var{N}(i,j)} is the expected
+## number of visits to transient state @var{j} before absorption, if it
+## is started in transient state @var{i}. The initial state is counted
+## if @math{i = j}. When called with two arguments, @var{N} is a vector
+## of size @math{N} such that @code{@var{N}(j)} is the expected number
+## of visits to transient state @var{j} before absorption, given initial
+## state occupancy probability @var{P0}.
+##
+## @item B
+## When called with a single argument, @var{B} is a @math{N \times N}
+## matrix where @code{@var{B}(i,j)} is the probability of being absorbed
+## in state @math{j}, starting from transient state @math{i}; if
+## @math{j} is not absorbing, @code{@var{B}(i,j) = 0}; if @math{i}
+## is absorbing, @code{@var{B}(i,i) = 1} and
+## @code{@var{B}(i,j) = 0} for all @math{j \neq j}. When called with
+## two arguments, @var{B} is a vector of size @math{N} where
+## @code{@var{B}(j)} is the probability of being absorbed in state
+## @var{j}, given initial state occupancy probabilities @var{p0}.
+##
+## @end table
+##
+## @seealso{ctmcmtta}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [t N B] = dtmcmtta( P, p0 )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin < 1 || nargin > 2 )
+    print_usage();
+  endif
+
+  [K err] = dtmcchkP(P);
+
+  (K>0) || ...
+      error(err);
+  
+  if ( nargin == 2 )
+    ( isvector(p0) && length(p0) == K && all(p0>=0) && abs(sum(p0)-1.0)<epsilon ) || ...
+	error( "p0 must be a state occupancy probability vector" );
+  endif
+
+  ## identify transient states
+  tr = find(diag(P) < 1);
+  ab = find(diag(P) == 1);
+  k = length(tr); # number of transient states
+  if ( k == K )
+    error("There are no absorbing states");
+  endif
+
+  N = B = zeros(size(P));
+  t = zeros(1,rows(P));
+
+  ## Source: Grinstead, Charles M.; Snell, J. Laurie (July 1997). "Ch.
+  ## 11: Markov Chains". Introduction to Probability. American
+  ## Mathematical Society. ISBN 978-0821807491.
+  ## http://www.cs.virginia.edu/~gfx/Courses/2006/DataDriven/bib/texsyn/Chapter11.pdf
+
+  tmpN = inv(eye(k) - P(tr,tr)); # matrix N = (I-Q)^-1
+  N(tr,tr) = tmpN;
+  R = P(tr,ab);
+
+  res = tmpN * ones(k,1);
+  t(tr) = res;
+
+  tmp = tmpN*R;
+  B(tr,ab) = tmp;
+  ## set B(i,i) = 1 for all absorbing states i
+  dd = diag(B);
+  dd(ab) = 1;
+  B(1:K+1:end) = dd;
+
+  if ( nargin == 2 )
+    t = dot(p0,t);
+    N = p0*N;
+    B = p0*B;
+  endif
+
+endfunction
+%!test
+%! fail( "dtmcmtta(1,2,3)" );
+%! fail( "dtmcmtta()" );
+
+%!test
+%! P = dtmcbd([0 .5 .5 .5], [.5 .5 .5 0]);
+%! [t N B] = dtmcmtta(P);
+%! assert( t, [0 3 4 3 0], 10*eps );
+%! assert( B([2 3 4],[1 5]), [3/4 1/4; 1/2 1/2; 1/4 3/4], 10*eps );
+%! assert( B(1,1), 1 );
+%! assert( B(5,5), 1 );
+
+%!test
+%! P = dtmcbd([0 .5 .5 .5], [.5 .5 .5 0]);
+%! [t N B] = dtmcmtta(P);
+%! assert( t(3), 4, 10*eps );
+%! assert( B(3,1), 0.5, 10*eps );
+%! assert( B(3,5), 0.5, 10*eps );
+
+## Example on p. 422 of [GrSn97]
+%!test
+%! P = dtmcbd([0 .5 .5 .5 .5], [.5 .5 .5 .5 0]);
+%! [t N B] = dtmcmtta(P);
+%! assert( t(2:5), [4 6 6 4], 100*eps );
+%! assert( B(2:5,1), [.8 .6 .4 .2]', 100*eps );
+%! assert( B(2:5,6), [.2 .4 .6 .8]', 100*eps );
+
+## Compute the probability of completing the "snakes and ladders"
+## game in n steps, for various values of n. Also, computes the expected
+## number of steps which are necessary to complete the game.
+## Source: 
+## http://mapleta.emich.edu/aross15/coursepack3419/419/ch-04/chutes-and-ladders.pdf
+%!demo
+%! n = 6;
+%! P = zeros(101,101);
+%! for j=0:(100-n)
+%!   i=1:n;
+%!   P(1+j,1+j+i) = 1/n;
+%! endfor  
+%! for j=(101-n):100 
+%!   P(1+j,1+j) = (n-100+j)/n;
+%! endfor
+%! for j=(101-n):100
+%!   i=1:(100-j);
+%!   P(1+j,1+j+i) = 1/n;
+%! endfor
+%! Pstar = P;
+%! ## setup snakes and ladders
+%! SL = [1 38; ...
+%!       4 14; ...
+%!       9 31; ...
+%!       16 6; ...
+%!       21 42; ...
+%!       28 84; ...
+%!       36 44; ...
+%!       47 26; ...
+%!       49 11; ...
+%!       51 67; ...
+%!       56 53; ...
+%!       62 19; ...
+%!       64 60; ...
+%!       71 91; ...
+%!       80 100; ...
+%!       87 24; ...
+%!       93 73; ...
+%!       95 75; ...
+%!       98 78 ];
+%! for ii=1:rows(SL);
+%!   i = SL(ii,1);
+%!   j = SL(ii,2);
+%!   Pstar(1+i,:) = 0;
+%!   for k=0:100
+%!     if ( k != i )
+%!       Pstar(1+k,1+j) = P(1+k,1+j) + P(1+k,1+i);
+%!     endif
+%!   endfor
+%!   Pstar(:,1+i) = 0;
+%! endfor
+%! Pstar += diag( 1-sum(Pstar,2) );
+%! # spy(Pstar); pause
+%! nsteps = 250; # number of steps
+%! Pfinish = zeros(1,nsteps); # Pfinish(i) = probability of finishing after step i
+%! pstart = zeros(1,101); pstart(1) = 1; pn = pstart;
+%! for i=1:nsteps
+%!   pn = pn*Pstar;
+%!   Pfinish(i) = pn(101); # state 101 is the ending (absorbing) state
+%! endfor
+%! f = dtmcmtta(Pstar,pstart);
+%! printf("Average number of steps to complete the game: %f\n", f );
+%! plot(Pfinish,"linewidth",2);
+%! line([f,f],[0,1]);
+%! text(f*1.1,0.2,["Mean Time to Absorption (" num2str(f) ")"]);
+%! xlabel("Step number (n)");
+%! title("Probability of finishing the game before step n");
+
+## "Rat maze" problem (p. 453 of [GrSn97]);
+%!test
+%! P = zeros(9,9);
+%! P(1,[2 4]) = .5;
+%! P(2,[1 5 3]) = 1/3;
+%! P(3,[2 6]) = .5;
+%! P(4,[1 5 7]) = 1/3;
+%! P(5,:) = 0; P(5,5) = 1;
+%! P(6,[3 5 9]) = 1/3;
+%! P(7,[4 8]) = .5;
+%! P(8,[7 5 9]) = 1/3;
+%! P(9,[6 8]) = .5;
+%! t = dtmcmtta(P);
+%! assert( t, [6 5 6 5 0 5 6 5 6], 10*eps );
+
diff --git a/inst/dtmctaexps.m b/inst/dtmctaexps.m
new file mode 100644
index 0000000..7d599e3
--- /dev/null
+++ b/inst/dtmctaexps.m
@@ -0,0 +1,87 @@
+## Copyright (C) 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} =} dtmctaexps (@var{P}, @var{n}, @var{p0})
+## @deftypefnx {Function File} {@var{L} =} dtmctaexps (@var{P}, @var{p0})
+##
+## @cindex time-alveraged sojourn time, DTMC
+## @cindex discrete time Markov chain
+## @cindex Markov chain, discrete time
+## @cindex DTMC
+##
+## Compute the @emph{time-averaged sojourn time} @code{@var{M}(i)},
+## defined as the fraction of time steps @math{@{0, 1, @dots{}, n@}} (or
+## until absorption) spent in state @math{i}, assuming that the state
+## occupancy probabilities at time 0 are @var{p0}.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @math{N \times N} transition probability matrix.
+##
+## @item n
+## Number of transitions during which the time-averaged expected sojourn times
+## are computed (@math{@var{n} @geq{} 0}). if @math{@var{n} = 0},
+## returns @var{p0}.
+##
+## @item p0
+## Initial state occupancy probabilities.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item M
+## If this function is called with three arguments, @code{@var{M}(i)} is
+## the expected fraction of steps @math{@{0, 1, @dots{}, n@}} spent in
+## state @math{i}, assuming that the state occupancy probabilities at
+## time zero are @var{p0}. If this function is called with two
+## arguments, @code{@var{M}(i)} is the expected fraction of steps spent
+## in state @math{i} until absorption.
+##
+## @end table
+##
+## @seealso{ctmctaexps}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function M = dtmctaexps( P, varargin )
+
+  persistent epsilon = 10*eps;
+
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+
+  L = dtmcexps(P,varargin{:});
+  M = L ./ sum(L);
+endfunction
+%!test
+%! P = dtmcbd([1 1 1 1], [0 0 0 0]);
+%! p0 = [1 0 0 0 0];
+%! L = dtmctaexps(P,p0);
+%! assert( L, [.25 .25 .25 .25 0], 10*eps );
+
diff --git a/inst/engset.m b/inst/engset.m
new file mode 100644
index 0000000..6c3570d
--- /dev/null
+++ b/inst/engset.m
@@ -0,0 +1,123 @@
+## Copyright (C) 2014 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{B} =} engset (@var{A}, @var{m}, @var{n})
+##
+## @cindex Engset loss formula
+##
+## Compute the Engset blocking probability @math{P_b(A, m, n)} for a system
+## with a finite population of @math{n} users, @math{m} identical
+## servers, no queue, individual service rate @math{\mu}, individual
+## arrival rate @math{\lambda} (i.e., the time until a user tries to
+## request service is exponentially distributed with mean @math{1 /
+## \lambda}), and offered load @math{A = \lambda / \mu}.
+## 
+## @tex
+## @math{P_b(A, m, n)} is defined for @math{n > m} as:
+##
+## $$
+## P_b(A, m, n) = {{\displaystyle{A^m {n \choose m}}} \over {\displaystyle{\sum_{k=0}^m A^k {n \choose k}}}}
+## $$
+##
+## and is 0 if @math{n @leq{} m}.
+## @end tex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item A
+## Offered load, defined as @math{A = \lambda / \mu} where
+## @math{\lambda} is the mean arrival rate and @math{\mu} the mean
+## service rate of each individual server (real, @math{A > 0}).
+##
+## @item m
+## Number of identical servers (integer, @math{m @geq{} 1}). Default @math{m = 1}
+##
+## @item n
+## Number of requests (integer, @math{n @geq{} 1}). Default @math{n = 1}
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item B
+## The value @math{P_b(A, m, n)}
+##
+## @end table
+##
+## @var{A}, @var{m} or @math{n} can be vectors, and in this case, the
+## results will be vectors as well.
+##
+## @seealso{erlangb, erlangc}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+function P = engset(A, m, n)
+  if ( nargin < 1 || nargin > 3 )
+    print_usage();
+  endif
+
+  ( isnumeric(A) && all( A(:) > 0 ) ) || error("A must be positive");
+  
+  if ( nargin < 2 )
+    m = 1;
+  else
+    ( isnumeric(m) && all( fix(m(:)) == m(:)) && all( m(:) > 0 ) ) || error("m must be a positive integer");
+  endif
+
+  if ( nargin < 3 )
+    n = 1;
+  else
+    ( isnumeric(n) && all( fix(n(:)) == n(:)) && all( n(:) > 0 ) ) || error("n must be a positive integer");
+  endif
+  
+  [err A m n] = common_size(A, m, n);
+  if ( err )
+    error("parameters are not of common size");
+  endif
+
+  P = arrayfun( @__engset_compute, A, m, n);
+endfunction
+
+## Compute P_b(A,m,n) recursively
+function P = __engset_compute(A, m, n)
+  if ( m >= n )
+    P = 0.0;
+  else
+    P = 1.0;
+    for i = 1:m
+      P=(A*(n-i)*P)/(i+A*(n-i)*P);
+    endfor
+  endif
+endfunction
+
+%!test
+%! fail("erlangb(1, -1)", "positive");
+%! fail("erlangb(-1, 1)", "positive");
+%! fail("erlangb(1, 0)", "positive");
+%! fail("erlangb(0, 1)", "positive");
+%! fail("erlangb('foo',1)", "positive");
+%! fail("erlangb(1,'bar')", "positive");
+%! fail("erlangb([1 1],[1 1 1])","common size");
+
diff --git a/inst/erlangb.m b/inst/erlangb.m
new file mode 100644
index 0000000..b820daf
--- /dev/null
+++ b/inst/erlangb.m
@@ -0,0 +1,115 @@
+## Copyright (C) 2014 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{B} =} erlangb (@var{A}, @var{m})
+##
+## @cindex Erlang-B formula
+##
+## Compute the value of the Erlang-B formula @math{E_B(A, m)} giving the
+## probability that an open system with @math{m} identical servers,
+## arrival rate @math{\lambda}, individual service rate @math{\mu}
+## and offered load @math{A = \lambda / \mu} has all servers busy.
+## 
+## @tex
+## @math{E_B(A, m)} is defined as:
+##
+## $$
+## E_B(A, m) = \displaystyle{{A^m \over m!} \left( \sum_{k=0}^m {A^k \over k!} \right) ^{-1}}
+## $$
+## @end tex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item A
+## Offered load, defined as @math{A = \lambda / \mu} where
+## @math{\lambda} is the mean arrival rate and @math{\mu} the mean
+## service rate of each individual server (real, @math{A > 0}).
+##
+## @item m
+## Number of identical servers (integer, @math{m @geq{} 1}). Default @math{m = 1}
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item B
+## The value @math{E_B(A, m)}
+##
+## @end table
+##
+## @var{A} or @var{m} can be vectors, and in this case, the results will
+## be vectors as well.
+##
+## @seealso{qsmmm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+function B = erlangb(A, m)
+  if ( nargin < 1 || nargin > 2 )
+    print_usage();
+  endif
+
+  ( isnumeric(A) && all( A(:) > 0 ) ) || error("A must be positive");
+  
+  if ( nargin == 1 )
+    m = 1;
+  else
+    ( isnumeric(m) && all( fix(m(:)) == m(:)) && all( m(:) > 0 ) ) || error("m must be a positive integer");
+  endif
+
+  [err A m] = common_size(A, m);
+  if ( err )
+    error("parameters are not of common size");
+  endif
+
+  B = arrayfun( @__erlangb_compute, A, m);
+endfunction
+
+## Compute E_B(A,m) recursively, as described in:
+##
+## Guoping Zeng, Two common properties of the erlang-B function,
+## erlang-C function, and Engset blocking function, Mathematical and
+## Computer Modelling Volume 37, Issues 12–13, June 2003, Pages
+## 1287–1296 http://dx.doi.org/10.1016/S0895-7177(03)90040-9
+##
+## To improve numerical stability, the recursion is based on the inverse
+## 1 / E_B rather than E_B itself.
+function B = __erlangb_compute(A, m)
+  Binv = 1.0;
+  for k=1:m
+    Binv = 1.0 + k/A*Binv;
+  endfor
+  B = 1.0 / Binv;
+endfunction
+
+%!test
+%! fail("erlangb(1, -1)", "positive");
+%! fail("erlangb(-1, 1)", "positive");
+%! fail("erlangb(1, 0)", "positive");
+%! fail("erlangb(0, 1)", "positive");
+%! fail("erlangb('foo',1)", "positive");
+%! fail("erlangb(1,'bar')", "positive");
+%! fail("erlangb([1 1],[1 1 1])","common size");
+
diff --git a/inst/erlangc.m b/inst/erlangc.m
new file mode 100644
index 0000000..0187c61
--- /dev/null
+++ b/inst/erlangc.m
@@ -0,0 +1,97 @@
+## Copyright (C) 2014 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{C} =} erlangc (@var{A}, @var{m})
+##
+## @cindex Erlang-C formula
+##
+## Compute the steady-state probability @math{E_C(A, m)} that an open
+## queueing system with @math{m} identical servers, infinite wating
+## space, arrival rate @math{\lambda}, individual service rate
+## @math{\mu} and offered load @math{A = \lambda / \mu} has all the
+## servers busy.
+##
+## @tex
+## @math{E_C(A, m)} is defined as:
+##
+## $$
+## E_C(A, m) = \displaystyle{ {A^m \over m!} {1 \over 1-\rho} \left( \sum_{k=0}^{m-1} {A^k \over k!} + {A^m \over m!} {1 \over 1 - \rho} \right) ^{-1}}
+## $$
+##
+## where @math{\rho = A / m = \lambda / (m \mu)}.
+## @end tex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item A Offered load. @math{A = \lambda / \mu} where
+## @math{\lambda} is the mean arrival rate and @math{\mu} the mean
+## service rate of each individual server (real, @math{0 < A < m}).
+##
+## @item m Number of identical servers (integer, @math{m @geq{} 1}).
+## Default @math{m = 1}
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item B The value @math{E_C(A, m)}
+##
+## @end table
+##
+## @var{A} or @var{m} can be vectors, and in this case, the results will
+## be vectors as well.
+##
+## @seealso{qsmmm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+function C = erlangc(A, m)
+  if ( nargin < 1 || nargin > 2 )
+    print_usage();
+  endif
+
+  ( isnumeric(A) && all(A(:) > 0) ) || error("A must be positive");
+   
+  if ( nargin == 1 )
+    m = 1;
+  else
+    ( isnumeric(m) && all( fix(m(:)) == m(:)) && all( m(:) > 0 ) ) || error("m must be a positive integer");
+  endif
+  
+  [err A, m] = common_size(A, m);
+  if ( err )
+    error("parameters are not of common size");
+  endif
+  
+  all( A(:) < m(:) ) || error("A must be < m");
+   
+  rho = A ./ m;
+  B = erlangb(A, m);
+  C = B ./ (1 - rho .* (1 - B));
+endfunction
+
+%!test
+%! fail("erlangc('foo',1)", "positive");
+%! fail("erlangc(1,'bar')", "positive");
diff --git a/inst/population_mix.m b/inst/population_mix.m
new file mode 100644
index 0000000..a5e8d99
--- /dev/null
+++ b/inst/population_mix.m
@@ -0,0 +1,39 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncmpopmix} instead.
+##
+## @seealso{qncmpopmix}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function pop_mix = population_mix( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "population_mix is deprecated. Please use qncmpopmix instead");
+  endif
+  pop_mix = qncmpopmix( varargin{:} );
+endfunction
diff --git a/inst/private/expn.m b/inst/private/expn.m
new file mode 100644
index 0000000..012b9d4
--- /dev/null
+++ b/inst/private/expn.m
@@ -0,0 +1,32 @@
+## Copyright (C) 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{r} =} expn (@var{a}, @var{n})
+##
+## Compute @code{r = a^n / n!}, with @math{a>0} and @math{n @geq{} 0}.
+##
+## @end deftypefn
+function r = expn( a, n )
+  n>=0 || ...
+      error("n must be nonnegative");
+  a>0 || ...
+      error("a must be positive");
+  tmp = cumprod( [1 a./(1:n)] );
+  r = tmp(n+1);
+endfunction
\ No newline at end of file
diff --git a/inst/private/qncmchkparam.m b/inst/private/qncmchkparam.m
new file mode 100644
index 0000000..cb9beee
--- /dev/null
+++ b/inst/private/qncmchkparam.m
@@ -0,0 +1,111 @@
+## Copyright (C) 2012, 2013 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{err} @var{Nout} @var{Sout} @var{Vout} @var{mout} @var{Zout}] = } qncmchkparam( N, S )
+## @deftypefnx {Function File} {[@var{err} @var{Nout} @var{Sout} @var{Vout} @var{mout} @var{Zout}] = } qncmchkparam( N, S, V )
+## @deftypefnx {Function File} {[@var{err} @var{Nout} @var{Sout} @var{Vout} @var{mout} @var{Zout}] = } qncmchkparam( N, S, V, m )
+## @deftypefnx {Function File} {[@var{err} @var{Nout} @var{Sout} @var{Vout} @var{mout} @var{Zout}] = } qncmchkparam( N, S, V, m, Z )
+##
+## Validate input parameters for closed, multiclass network.
+## @var{err} is the empty string on success, or a suitable error message
+## string on failure.
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [err Nout Sout Vout mout Zout] = qncmchkparam( N, S, V, m, Z )
+  
+  err = "";
+  [Nout Sout Vout mout Zout] = deal(0);
+
+  if ( nargin < 2 || nargin > 5 )
+    err = "Wrong number of parameters (min 2, max 5)";
+    return;
+  endif
+
+  if ! ( isnumeric(N) && isvector(N) && length(N)>0 )
+    err = "N must be a nonempty vector";
+    return;
+  endif
+
+  if ! ( all(N>=0) && all( fix(N) == N ) )
+    err = "N must contain nonnegative integers";
+    return;
+  endif
+
+  Nout = N(:)';
+
+  C = length(Nout); ## Number of classes
+
+  if ! ( isnumeric(S) && ismatrix(S) && ndims(S) == 2 && rows(S) == C )
+    err = sprintf("S must be a 2-dimensional matrix with %d rows",C);
+    return;
+  endif
+
+  if ( any(S(:)<0) )
+    err = "S must contain nonnegative values";
+    return;
+  endif
+
+  Sout = S;
+
+  K = columns(Sout);
+
+  if ( nargin < 3 )
+    Vout = ones(size(Sout));
+  else
+    if ! ( isnumeric(V) && ismatrix(V) && ndims(V) == 2 && rows(V) == C && columns(V) == K )
+      err = sprintf("V must be a %d x %d matrix", C, K );
+      return;
+    endif
+
+    if ( any(V(:)<0) )
+      err = "V must contain nonnegative values";
+      return;
+    endif
+
+    Vout = V;
+  endif
+
+  if ( nargin < 4 ) 
+    mout = ones(1,K);
+  else
+    if ! ( isnumeric(m) && isvector(m) && length(m) == K ) 
+      err = sprintf("m must be a vector with %d elements", K );
+      return;
+    endif
+    mout = m(:)';
+  endif
+
+  if ( nargin < 5 )
+    Zout = zeros(1,C);
+  else
+    if ! ( isnumeric(Z) && isvector(Z) && length(Z) == C )
+      err = sprintf("Z must be a vector with %d elements", C);
+      return;
+    endif
+    if ( any(Z<0) )
+      err = "Z must contain nonnegative values";
+      return;
+    endif
+    Zout = Z(:)';
+  endif
+endfunction
diff --git a/inst/private/qncschkparam.m b/inst/private/qncschkparam.m
new file mode 100644
index 0000000..378fb6d
--- /dev/null
+++ b/inst/private/qncschkparam.m
@@ -0,0 +1,99 @@
+## Copyright (C) 2012, 2013, 2014 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{err} @var{Nout} @var{Sout} @var{Vout} @var{mout} @var{Zout}] = } qncschkparam( N, S, V, m, Z )
+##
+## Validate input parameters for closed, single class networks.
+## @var{err} is the empty string on success, or a suitable error message
+## string on failure.
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [err Nout Sout Vout mout Zout] = qncschkparam( N, S, V, m, Z )
+  
+  err = "";
+  [Nout Sout Vout mout Zout] = deal(0);
+
+  if ( nargin < 2 || nargin > 5)
+    err = "Wrong number of parameters (min 2, max 5)";
+    return;
+  endif
+
+  if ! ( isscalar(N) && N>=0 && N == fix(N) )
+    err = "N must be a nonnegative integer";  
+    return;
+  endif
+
+  Nout = N;
+
+  if ! ( isnumeric(S) && isvector(S) && length(S)>0 )
+    err = "S must be a nonempty vector";
+    return;
+  endif
+
+  if ( any(S<0) )
+    err = "S must contain nonnegative values";
+    return;
+  endif
+
+  Sout = S(:)';
+
+  if ( nargin < 3 )
+    Vout = ones(size(Sout));
+  else
+    if ! ( isnumeric(V) && isvector(V) )
+      err =  "V must be a vector";
+      return;
+    endif
+    if ( any(V<0) )
+      err =  "V must contain nonnegative values";
+      return;
+    endif
+    Vout = V(:)';
+  endif
+
+  if ( nargin < 4 ) 
+    mout = ones(size(Sout));
+  else
+    if ! ( isnumeric(V) && isvector(m) )
+      err = "m must be a vector";
+      return;
+    endif
+    mout = m(:)';
+  endif
+
+  [er Sout Vout mout] = common_size(Sout, Vout, mout);
+  if (er != 0 )
+    err = "S, V and m are of incompatible size";
+    return;
+  endif
+
+  if ( nargin < 5 )
+    Zout = 0;
+  else
+    if ! ( isscalar(Z) && Z >= 0 )
+      err = "Z must be a nonnegative scalar";
+      return;
+    endif
+    Zout = Z;
+  endif
+endfunction
diff --git a/inst/private/qnomchkparam.m b/inst/private/qnomchkparam.m
new file mode 100644
index 0000000..62d7831
--- /dev/null
+++ b/inst/private/qnomchkparam.m
@@ -0,0 +1,95 @@
+## Copyright (C) 2012, 2013, 2014 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{err} @var{lambda} @var{S} @var{V} @var{m} @var{Z}] = } qnomchkparam( lambda, S, ... )
+##
+## Validate input parameters for open, multiclass network.
+## @var{err} is the empty string on success, or a suitable error message
+## string on failure.
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [err lambda S V m] = qnomchkparam( varargin )
+  
+  err = "";
+
+  assert( nargin >= 2 );
+
+  lambda = varargin{1};
+
+  S = varargin{2};
+
+  [V m] = deal(0);
+
+  if ! ( isnumeric(lambda) && isvector(lambda) && length(lambda)>0 )
+    err = "lambda must be a nonempty vector";
+    return;
+  endif
+
+  if ( any(lambda<0) )
+    err = "lambda must contain nonnegative values";
+    return;
+  endif
+
+  lambda = lambda(:)';
+
+  C = length(lambda); ## Number of classes
+
+  if ! ( isnumeric(S) && ismatrix(S) && ndims(S) == 2 && rows(S) == C )
+    err = sprintf("S must be a 2-dimensional matrix with %d rows",C);
+    return;
+  endif
+
+  if ( any(S(:)<0) )
+    err = "S must contain nonnegative values";
+    return;
+  endif
+
+  K = columns(S);
+
+  if ( nargin < 3 )
+    V = ones(size(S));
+  else
+    V = varargin{3};
+    if ! ( isnumeric(V) && ismatrix(V) & ndims(V) == 2 & rows(V) == C & columns(V) == K )
+      err = sprintf("V must be a %d x %d matrix", C, K );
+      return;
+    endif
+
+    if ( any(V(:)<0) )
+      err = "V must contain nonnegative values";
+      return;
+    endif
+  endif
+
+  if ( nargin < 4 ) 
+    m = ones(1,K);
+  else
+    m = varargin{4};
+    if ! ( isnumeric(m) && isvector(m) && length(m) == K ) 
+      err = sprintf("m must be a vector with %d elements", K );
+      return;
+    endif
+    m = m(:)';
+  endif
+
+endfunction
diff --git a/inst/private/qnoschkparam.m b/inst/private/qnoschkparam.m
new file mode 100644
index 0000000..7e983cc
--- /dev/null
+++ b/inst/private/qnoschkparam.m
@@ -0,0 +1,94 @@
+## Copyright (C) 2012, 2013, 2014 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{err} @var{lambda} @var{S} @var{V} @var{m}] = } qnoschkparam( lambda, S, V, m )
+##
+## Validate input parameters for open, single class networks.
+## @var{err} is the empty string on success, or a suitable error message
+## string on failure.
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [err lambda S V m] = qnoschkparam( varargin )
+  
+  err = "";
+
+  assert( nargin >= 2 );
+
+  lambda = varargin{1};
+
+  S = varargin{2};
+
+  [V m] = deal(0);
+
+  if ! ( isscalar(lambda) && lambda > 0 )
+    err = "lambda must be a positive scalar";  
+    return;
+  endif
+
+  if ! ( isnumeric(S) && isvector(S) && length(S)>0 )
+    err = "S must be a nonempty vector";
+    return;
+  endif
+
+  if ( any(S<0) )
+    err = "S must contain nonnegative values";
+    return;
+  endif
+
+  S = S(:)';
+
+  if ( nargin < 3 )
+    V = ones(size(S));
+  else
+    V = varargin{3};
+
+    if ! ( isnumeric(V) && isvector(V) )
+      err =  "V must be a vector";
+      return;
+    endif
+    if ( any(V<0) )
+      err =  "V must contain nonnegative values";
+      return;
+    endif
+    V = V(:)';
+  endif
+
+  if ( nargin < 4 ) 
+    m = ones(size(S));
+  else
+    m = varargin{4};
+
+    if ! ( isnumeric(m) && isvector(m) )
+      err = "m must be a vector";
+      return;
+    endif
+    m = m(:)';
+  endif
+
+  [er S V m] = common_size(S, V, m);
+  if (er != 0 )
+    err = "S, V and m are of incompatible size";
+    return;
+  endif
+
+endfunction
diff --git a/inst/private/sumexpn.m b/inst/private/sumexpn.m
new file mode 100644
index 0000000..1fdbbf2
--- /dev/null
+++ b/inst/private/sumexpn.m
@@ -0,0 +1,58 @@
+## Copyright (C) 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{r} =} sumexpn (@var{a}, @var{n})
+##
+## Compute the sum:
+## 
+## @iftex
+## @tex
+## $$ S(a,n) = \sum_{k=0}^n {a^k \over k!} $$
+## @end tex
+## @end iftex
+## @ifnottex
+## @example
+##           n
+##           __ 
+##          \    a^k
+## S(a,n) =  >  -----      
+##          /__   k!
+##          k=0
+## @end example
+## @end ifnottex
+##
+## with @math{a>0} and @math{n @geq{} 0}.
+##
+## @end deftypefn
+function r = sumexpn( a, n )
+  n>=0 || ...
+      error("n must be nonnegative");
+  a>0 || ...
+      error("a must be positive");
+  r = sum(cumprod([1 a./(1:n)]));
+endfunction
+%!test
+%! a = 0.8;
+%! n = 0;
+%! assert( sumexpn(a,n), sum(a.^(0:n) ./ factorial(0:n)), 1e-6 );
+
+%!test
+%! a = 1.2;
+%! n = 6;
+%! assert( sumexpn(a,n), sum(a.^(0:n) ./ factorial(0:n)), 1e-6 );
\ No newline at end of file
diff --git a/inst/qnammm.m b/inst/qnammm.m
new file mode 100644
index 0000000..5011bf4
--- /dev/null
+++ b/inst/qnammm.m
@@ -0,0 +1,45 @@
+## 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})
+##
+## This function is deprecated. Please use @code{qsammm} instead.
+##
+## @seealso{qsammm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pm] = qnammm( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnammm is deprecated. Please use qsammm instead");
+  endif
+  [U R Q X p0 pm] = qsammm( varargin{:} );
+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 );
+
+
diff --git a/inst/qnclosed.m b/inst/qnclosed.m
new file mode 100644
index 0000000..140fce8
--- /dev/null
+++ b/inst/qnclosed.m
@@ -0,0 +1,95 @@
+## 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, single class
+## @cindex closed network, multiple classes
+##
+## 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{qncsemva}, @code{qncsmvald} or @code{qncmmva}.
+##
+## @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{qncsmva} which supports load-independent
+## service centers. If @var{S} is a matrix, @code{@var{S}(k,i)} is the
+## average service time at center @math{k} when @math{i=1, @dots{}, N}
+## jobs are present; in this case, the network is analyzed with the
+## @code{qncmmvald} 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{qncmmva} function.
+##
+## @end itemize
+##
+## @seealso{qncsmva, qncsmvald, qncmmva}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)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] = qncsmva( N, S, V, varargin{:} );
+    else
+      [U R Q X] = qncsmvald( N, S, V, varargin{:} );
+    endif
+  else
+    [U R Q X] = qncmmva( 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(size(S));             # All centers are single-server
+%! Z = 2;                         # External delay
+%! N = 15;                        # Maximum population to consider
+%! V = qncsvisits(P);             # Compute number of visits
+%! X_bsb_lower = X_bsb_upper = X_ab_lower = X_ab_upper = X_mva = zeros(1,N);
+%! for n=1:N
+%!   [X_bsb_lower(n) X_bsb_upper(n)] = qncsbsb(n, S, V, m, Z);
+%!   [X_ab_lower(n) X_ab_upper(n)] = qncsaba(n, S, V, m, 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]); legend("location","southeast");
+%! xlabel("Number of Requests n"); ylabel("System Throughput X(n)");
+
diff --git a/inst/qnclosedab.m b/inst/qnclosedab.m
new file mode 100644
index 0000000..6855093
--- /dev/null
+++ b/inst/qnclosedab.m
@@ -0,0 +1,57 @@
+## 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}, @dots{})
+##
+## This function is deprecated. Please use @code{qncsaba} instead.
+##
+## @seealso{qncsaba}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xl Xu Rl Ru] = qnclosedab( N, D, Z )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnclosedab is deprecated. Please use qncsaba instead");
+  endif
+  if ( nargin < 3 )
+    [Xl Xu Rl Ru] = qncsaba( N, D );
+  else
+    [Xl Xu Rl Ru] = qncsaba( N, D, ones(size(D)), ones(size(D)), Z );
+  endif
+endfunction
+
+%!test
+%! fail("qnclosedab(-1,0)", "N must be");
+%! fail("qnclosedab(1,[])", "nonempty");
+%! fail("qnclosedab(1,[-1 2])", "nonnegative");
+%! fail("qnclosedab(1,[1 2 3],-1)", "nonnegative");
+
+## 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 );
diff --git a/inst/qnclosedbsb.m b/inst/qnclosedbsb.m
new file mode 100644
index 0000000..e4709a7
--- /dev/null
+++ b/inst/qnclosedbsb.m
@@ -0,0 +1,49 @@
+## Copyright (C) 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}, @dots{} )
+##
+## This function is deprecated. Please use @code{qncsbsb} instead.
+##
+## @seealso{qncsbsb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xl Xu Rl Ru] = qnclosedbsb( N, D, Z )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnclosedbsb is deprecated. Please use qncsbsb instead");
+  endif
+  if ( nargin < 3 )
+    [Xl Xu Rl Ru] = qncsbsb(N, D);
+  else
+    [Xl Xu Rl Ru] = qncsbsb(N, D, ones(size(D)), ones(size(D)), Z);
+  endif
+endfunction
+%!test
+%! fail("qnclosedbsb(-1,0)", "N must be");
+%! fail("qnclosedbsb(1,[])", "nonempty");
+%! fail("qnclosedbsb(1,[-1 2])", "nonnegative");
+%! fail("qnclosedbsb(1,[1 2 3],-1)", "nonnegative");
+
diff --git a/inst/qnclosedgb.m b/inst/qnclosedgb.m
new file mode 100644
index 0000000..9812337
--- /dev/null
+++ b/inst/qnclosedgb.m
@@ -0,0 +1,43 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncsgb} instead.
+##
+## @seealso{qncsgb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_lower X_upper Q_lower Q_upper] = qnclosedgb( N, D, Z )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "Function qnclosedgb is deprecated. Please use qncsgb instead");
+  endif
+  if ( nargin < 3 ) 
+    [X_lower X_upper R_lower R_upper Q_lower Q_upper] = qncsgb( N, D );
+  else
+    [X_lower X_upper R_lower R_upper Q_lower Q_upper] = qncsgb( N, D, ones(size(D)), ones(size(D)), Z);
+  endif
+endfunction
diff --git a/inst/qnclosedmultimva.m b/inst/qnclosedmultimva.m
new file mode 100644
index 0000000..cb76eee
--- /dev/null
+++ b/inst/qnclosedmultimva.m
@@ -0,0 +1,44 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncmmva} instead.
+##
+## @seealso{qncmmva}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnclosedmultimva( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnclosedmultimva is deprecated. Please use qncmmva instead");
+  endif
+  [U R Q X] = qncmmva( varargin{:} );
+endfunction
diff --git a/inst/qnclosedmultimvaapprox.m b/inst/qnclosedmultimvaapprox.m
new file mode 100644
index 0000000..6491b15
--- /dev/null
+++ b/inst/qnclosedmultimvaapprox.m
@@ -0,0 +1,66 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncmmvaap} instead.
+##
+## @seealso{qncmmvaap}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnclosedmultimvaapprox( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qqnclosedmultimvaapprox is deprecated. Please use qncmmvaap instead");
+  endif
+  [U R Q X] = qncmmvaap( varargin{:} );
+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 );
+
diff --git a/inst/qnclosedpb.m b/inst/qnclosedpb.m
new file mode 100644
index 0000000..0d129f7
--- /dev/null
+++ b/inst/qnclosedpb.m
@@ -0,0 +1,40 @@
+## Copyright (C) 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}] =} qnclosedpb (@var{N}, @var{D} )
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnclosedpb (@var{N}, @var{D}, @var{Z} )
+##
+## This function is deprecated. Please use @code{qncspb} instead.
+##
+## @seealso{qncspb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_lower X_upper] = qnclosedpb( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "Function qnclosedpb is deprecated. Please use qncspb instead");
+  endif
+  [X_lower X_upper R_lower R_upper] = qncspb( varargin{:} );
+endfunction
diff --git a/inst/qnclosedsinglemva.m b/inst/qnclosedsinglemva.m
new file mode 100644
index 0000000..6a49b0b
--- /dev/null
+++ b/inst/qnclosedsinglemva.m
@@ -0,0 +1,41 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncsmva} instead.
+##
+## @seealso{qncsmva}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X G] = qnclosedsinglemva( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnclosedsinglemva is deprecated. Please use qncsmva instead");
+  endif
+  [U R Q X G] = qncsmva( varargin{:} );
+endfunction
diff --git a/inst/qnclosedsinglemvaapprox.m b/inst/qnclosedsinglemvaapprox.m
new file mode 100644
index 0000000..3edb98b
--- /dev/null
+++ b/inst/qnclosedsinglemvaapprox.m
@@ -0,0 +1,64 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncsmvaap} instead.
+##
+## @seealso{qncsmvaap}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnclosedsinglemvaapprox( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnclosedsinglemvaapprox is deprecated. Please use qncsmvaap instead");
+  endif
+  [U R Q X] = qncsmvaap( varargin{:} );
+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 );
+
diff --git a/inst/qnclosedsinglemvald.m b/inst/qnclosedsinglemvald.m
new file mode 100644
index 0000000..4fa2e89
--- /dev/null
+++ b/inst/qnclosedsinglemvald.m
@@ -0,0 +1,40 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncsmvald} instead.
+##
+## @seealso{qncsmvald}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnclosedsinglemvald( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnclosedsinglemvald is deprecated. Please use qncsmvald instead");
+  endif
+  [U R Q X] = qncsmvald( varargin{:} );
+endfunction
diff --git a/inst/qncmaba.m b/inst/qncmaba.m
new file mode 100644
index 0000000..7f5ac95
--- /dev/null
+++ b/inst/qncmaba.m
@@ -0,0 +1,162 @@
+## Copyright (C) 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}] =} qncmaba (@var{N}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmaba (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmaba (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmaba (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+##
+## @cindex bounds, asymptotic
+## @cindex asymptotic bounds
+## @cindex closed network
+## @cindex multiclass network, closed
+## @cindex closed multiclass network
+##
+## Compute Asymptotic Bounds for multiclass networks.
+## Single-server and infinite-server nodes are supported.
+## Multiple-server nodes and general load-dependent servers are not
+## supported.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## @code{@var{N}(c)} is the number of class @math{c} requests in the system.
+##
+## @item D
+## @code{@var{D}(c, k)} is class @math{c} service demand
+## at center @math{k} (@code{@var{D}(c,k) @geq{} 0}).
+##
+## @item S
+## @code{@var{S}(c, k)} is the mean service time of class @math{c}
+## requests 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 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 M/M/1-FCFS server.
+## This function does not support multiple-server nodes. Default
+## is 1.
+##
+## @item Z
+## @code{@var{Z}(c)} is class @math{c} external delay
+## (@code{@var{Z}(c) @geq{} 0}). Default is 0.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @itemx Xu
+## Lower and upper class @math{c} throughput bounds.
+##
+## @item Rl
+## @itemx Ru
+## Lower and upper class @math{c} response time bounds.
+##
+## @end table
+##
+## @seealso{qnclosedsingleab}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xl Xu Rl Ru] = qncmaba( varargin )
+
+  if ( nargin<2 || nargin>5 )
+    print_usage();
+  endif
+
+  [err N S V m Z] = qncmchkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  all(m<=1) || ...
+      error("Multiple-server nodes are not supported");
+
+  if ( sum(N) == 0 ) # handle trivial case of empty network
+    Xl = Xu = Rl = Ru = zeros(size(S));
+  else
+    D = S .* V;
+    
+    Dc_single = sum(D(:,(m==1)),2)'; # class c demand on single-server nodes
+    Dc_delay = sum(D(:,(m<1)),2)'; # class c demand on delay centers
+    Dc = sum(D,2)'; # class c total demand
+    Dcmax = max(D,[],2)'; # maximum class c demand at any server
+    Xl = N ./ ( dot(N,Dc_single) .+ Dc_delay .+ Z);
+    Xu = min( 1./Dcmax, N ./ (Dc .+ Z) );
+    Rl = N ./ Xu .- Z;
+    Ru = N ./ Xl .- Z;
+  endif
+endfunction
+
+%!test
+%! fail("qncmaba([],[])", "nonempty");
+%! fail("qncmaba([1 0], [1 2 3])", "2 rows");
+%! fail("qncmaba([1 0], [1 2 3; 4 5 -1])", "nonnegative");
+%! fail("qncmaba([1 2], [1 2 3; 4 5 6], [1 2 3])", "2 x 3");
+%! fail("qncmaba([1 2], [1 2 3; 4 5 6], [1 2 3; 4 5 -1])", "nonnegative");
+%! fail("qncmaba([1 2], [1 2 3; 1 2 3], [1 2 3; 1 2 3], [1 1])", "3 elements");
+%! fail("qncmaba([1 2], [1 2 3; 1 2 3], [1 2 3; 1 2 3], [1 1 2])", "not supported");
+%! fail("qncmaba([1 2], [1 2 3; 1 2 3], [1 2 3; 1 2 3], [1 1 -1],[1 2 3])", "2 elements");
+%! fail("qncmaba([1 2], [1 2 3; 1 2 3], [1 2 3; 1 2 3], [1 1 -1],[1 -2])", "nonnegative");
+
+%!test
+%! [Xl Xu Rl Ru] = qncmaba([0 0], [1 2 3; 1 2 3]);
+%! assert( all(Xl(:) == 0) );
+%! assert( all(Xu(:) == 0) );
+%! assert( all(Rl(:) == 0) );
+%! assert( all(Ru(:) == 0) );
+
+%!demo
+%! S = [10 7 5 4; ...
+%!      5  2 4 6];
+%! NN=20;
+%! Xl = Xu = Rl = Ru = Xmva = Rmva = zeros(NN,2);
+%! for n=1:NN
+%!   N=[n,10];
+%!   [a b c d] = qncmaba(N,S);
+%!   Xl(n,:) = a; Xu(n,:) = b; Rl(n,:) = c; Ru(n,:) = d;
+%!   [U R Q X] = qncmmva(N,S,ones(size(S)));
+%!   Xmva(n,:) = X(:,1)'; Rmva(n,:) = sum(R,2)';
+%! endfor
+%! subplot(2,2,1);
+%! plot(1:NN,Xl(:,1), 1:NN,Xu(:,1), 1:NN,Xmva(:,1),";MVA;", "linewidth", 2);
+%! title("Class 1 throughput");
+%! subplot(2,2,2);
+%! plot(1:NN,Xl(:,2), 1:NN,Xu(:,2), 1:NN,Xmva(:,2),";MVA;", "linewidth", 2);
+%! title("Class 2 throughput");
+%! subplot(2,2,3);
+%! plot(1:NN,Rl(:,1), 1:NN,Ru(:,1), 1:NN,Rmva(:,1),";MVA;", "linewidth", 2);
+%! title("Class 1 response time");
+%! subplot(2,2,4);
+%! plot(1:NN,Rl(:,2), 1:NN,Ru(:,2), 1:NN,Rmva(:,2),";MVA;", "linewidth", 2);
+%! title("Class 2 response time");
+
+
+
diff --git a/inst/qncmbsb.m b/inst/qncmbsb.m
new file mode 100644
index 0000000..2950e1e
--- /dev/null
+++ b/inst/qncmbsb.m
@@ -0,0 +1,153 @@
+## Copyright (C) 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}] =} qncmbsb (@var{N}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmbsb (@var{N}, @var{S}, @var{V})
+##
+## @cindex bounds, balanced system
+## @cindex balanced system bounds
+## @cindex multiclass network, closed
+## @cindex closed multiclass network
+##
+## Compute Balanced System Bounds for multiclass networks.
+## Only single-server nodes are supported.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## @code{@var{N}(c)} is the number of class @math{c} requests in the system.
+##
+## @item D
+## @code{@var{D}(c, k)} is class @math{c} service demand 
+## at center @math{k} (@code{@var{D}(c,k) @geq{} 0}).
+##
+## @item S
+## @code{@var{S}(c, k)} is the mean service time of class @math{c}
+## requests 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}). 
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @itemx Xu
+## Lower and upper class @math{c} throughput bounds.
+##
+## @item Rl
+## @itemx Ru
+## Lower and upper class @math{c} response time bounds.
+##
+## @end table
+##
+## @seealso{qnclosedsinglebsb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xl Xu Rl Ru] = qncmbsb( varargin )
+
+  if ( nargin<2 || nargin>5 )
+    print_usage();
+  endif
+
+  [err N S V m Z] = qncmchkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  all(m<=1) || ...
+      error("Multiple-server nodes are not supported");
+
+  all(Z == 0 ) || ...
+      error("This function only supports Z=0");
+
+  if ( sum(N) == 0 ) # handle trivial case of empty network
+    Xl = Xu = Rl = Ru = zeros(size(S));
+  else
+    D = S .* V;
+    K = columns(S);
+    
+    ## Equations from T. Kerola, The Composite Bound Method (CBM) for
+    ## Computing Throughput Bounds in Multiple Class Environments},
+    ## Technical Report CSD-TR-475, Department of Computer Sciences,
+    ## Purdue University, mar 13 1984 (Revisted aug 27, 1984), available
+    ## at
+    ## http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1394&context=cstech
+
+    Dc = sum(D,2)';
+    D_max = max(D,[],2)';
+    D_min = min(D,[],2)';
+    Xl = N ./ (Dc .+ (sum(N)-1) .* D_max);
+    Xu = min( 1./D_max, N ./ ((K+sum(N)-1) .* D_min));
+    Rl = N ./ Xu;
+    Ru = N ./ Xl;
+  endif
+endfunction
+
+%!test
+%! fail("qncmbsb([],[])", "nonempty");
+%! fail("qncmbsb([1 0], [1 2 3])", "2 rows");
+%! fail("qncmbsb([1 0], [1 2 3; 4 5 -1])", "nonnegative");
+%! fail("qncmbsb([1 2], [1 2 3; 4 5 6], [1 2 3])", "2 x 3");
+%! fail("qncmbsb([1 2], [1 2 3; 4 5 6], [1 2 3; 4 5 -1])", "nonnegative");
+%! fail("qncmbsb([1 2], [1 2 3; 1 2 3], [1 2 3; 1 2 3], [1 1])", "3 elements");
+%! fail("qncmbsb([1 2], [1 2 3; 1 2 3], [1 2 3; 1 2 3], [1 1 2])", "not supported");
+%! fail("qncmbsb([1 2], [1 2 3; 1 2 3], [1 2 3; 1 2 3], [1 1 -1],[1 2 3])", "2 elements");
+%! fail("qncmbsb([1 2], [1 2 3; 1 2 3], [1 2 3; 1 2 3], [1 1 -1],[1 -2])", "nonnegative");
+%! fail("qncmbsb([1 2], [1 2 3; 1 2 3], [1 2 3; 1 2 3], [1 1 -1],[1 0])", "only supports");
+
+%!test
+%! [Xl Xu Rl Ru] = qncmbsb([0 0], [1 2 3; 1 2 3]);
+%! assert( all(Xl(:) == 0) );
+%! assert( all(Xu(:) == 0) );
+%! assert( all(Rl(:) == 0) );
+%! assert( all(Ru(:) == 0) );
+
+%!demo
+%! S = [10 7 5 4; ...
+%!      5  2 4 6];
+%! NN=20;
+%! Xl = Xu = Rl = Ru = Xmva = Rmva = zeros(NN,2);
+%! for n=1:NN
+%!   N=[n,10];
+%!   [a b c d] = qncmbsb(N,S);
+%!   Xl(n,:) = a; Xu(n,:) = b; Rl(n,:) = c; Ru(n,:) = d;
+%!   [U R Q X] = qncmmva(N,S,ones(size(S)));
+%!   Xmva(n,:) = X(:,1)'; Rmva(n,:) = sum(R,2)';
+%! endfor
+%! subplot(2,2,1);
+%! plot(1:NN,Xl(:,1), 1:NN,Xu(:,1), 1:NN,Xmva(:,1),";MVA;", "linewidth", 2);
+%! title("Class 1 throughput");
+%! subplot(2,2,2);
+%! plot(1:NN,Xl(:,2), 1:NN,Xu(:,2), 1:NN,Xmva(:,2),";MVA;", "linewidth", 2);
+%! title("Class 2 throughput");
+%! subplot(2,2,3);
+%! plot(1:NN,Rl(:,1), 1:NN,Ru(:,1), 1:NN,Rmva(:,1),";MVA;", "linewidth", 2);
+%! title("Class 1 response time");
+%! subplot(2,2,4);
+%! plot(1:NN,Rl(:,2), 1:NN,Ru(:,2), 1:NN,Rmva(:,2),";MVA;", "linewidth", 2);
+%! title("Class 2 response time");
diff --git a/inst/qncmcb.m b/inst/qncmcb.m
new file mode 100644
index 0000000..6bd31b5
--- /dev/null
+++ b/inst/qncmcb.m
@@ -0,0 +1,139 @@
+## Copyright (C) 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}] =} qncmcb (@var{N}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncmcb (@var{N}, @var{S}, @var{V})
+##
+## @cindex multiclass network, closed
+## @cindex closed multiclass network
+## @cindex bounds, composite
+## @cindex composite bounds
+##
+## Composite Bound (CB) on throughput and response time for closed multiclass networks.
+##
+## This function implements the Composite Bound Method described in T.
+## Kerola, @cite{The Composite Bound Method (CBM) for Computing
+## Throughput Bounds in Multiple Class Environments}, Technical Report
+## CSD-TR-475, Purdue University, march 13, 1984 (revised august 27,
+## 1984).
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## @code{@var{N}(c)} is the number of class @math{c} requests in the system.
+##
+## @item D
+## @code{@var{D}(c, k)} is class @math{c} service demand
+## at center @math{k} (@code{@var{S}(c,k) @geq{} 0}).
+##
+## @item S
+## @code{@var{S}(c, k)} is the mean service time of class @math{c}
+## requests 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}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @itemx Xu
+## Lower and upper class @math{c} throughput bounds.
+##
+## @item Rl
+## @itemx Ru
+## Lower and upper class @math{c} response time bounds.
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xl Xu Rl Ru] = qncmcb( varargin )
+
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+
+  [err N S V m Z] = qncmchkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  all(m == 1) || ...
+      error("this function only supports single-server FCFS centers");
+
+  all(Z == 0) || ...
+      error("this function does not support think time");
+
+  [C K] = size(S);
+
+  D = S .* V;
+
+  [Xl] = qncmbsb(N, D);
+  Xu = zeros(1,C);
+
+  D_max = max(D,[],2)';
+  for r=1:C # FIXME: vectorize this
+
+    ## This is equation (13) from T. Kerola, The Composite Bound Method
+    ## (CBM) for Computing Throughput Bounds in Multiple Class
+    ## Environments, Technical Report CSD-TR-475, Purdue University,
+    ## march 13, 1984 (revised august 27, 1984)
+    ## http://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1394&context=cstech
+
+    ## The only modification here is to apply also the upper bound
+    ## 1/D_max(r), eveb though it seems redundant.
+
+    s = (1:C != r); # boolean array
+    tmp = (1 .- Xl(s)*D(s,:)) ./ D(r,:);
+    Xu(r) = min([tmp 1/D_max(r)]);
+  endfor
+
+  Rl = N ./ Xu;
+  Ru = N ./ Xl;
+endfunction
+
+%!demo
+%! S = [10 7 5 4; ...
+%!      5  2 4 6];
+%! NN=20;
+%! Xl = Xu = Xmva = zeros(NN,2);
+%! for n=1:NN
+%!   N=[n,10];
+%!   [a b] = qncmcb(N,S);
+%!   Xl(n,:) = a; Xu(n,:) = b;
+%!   [U R Q X] = qncmmva(N,S,ones(size(S)));
+%!   Xmva(n,:) = X(:,1)';
+%! endfor
+%! subplot(2,1,1);
+%! plot(1:NN,Xl(:,1),"linewidth", 2, 1:NN,Xu(:,1),"linewidth", 2, ...
+%!      1:NN,Xmva(:,1),";MVA;");
+%! title("Class 1 throughput");
+%! subplot(2,1,2);
+%! plot(1:NN,Xl(:,2),"linewidth", 2, 1:NN,Xu(:,2), "linewidth", 2,...
+%!      1:NN,Xmva(:,2),";MVA;");
+%! title("Class 2 throughput");
+%! xlabel("Number of class 1 requests");
diff --git a/inst/qncmmva.m b/inst/qncmmva.m
new file mode 100644
index 0000000..079958a
--- /dev/null
+++ b/inst/qncmmva.m
@@ -0,0 +1,850 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 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}] =} qncmmva (@var{N}, @var{S} )
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{P})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{P}, @var{r})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmva (@var{N}, @var{S}, @var{P}, @var{r}, @var{m})
+##
+## @cindex Mean Value Analysys (MVA)
+## @cindex closed network, multiple classes
+## @cindex multiclass network, closed
+##
+## Compute steady-state performance measures for closed, multiclass
+## queueing networks 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 servers 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 for all @var{k},
+## @code{@var{U}(c,k) = @var{R}(c,k) = @var{Q}(c,k) = @var{X}(c,k) = 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}). If the
+## service time at center @math{k} is class-dependent, i.e., different
+## classes have different service times at center @math{k}, 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, i.e., all classes
+## @strong{must} have the same service time.
+##
+## @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 argument, 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; the reference stations for each class
+## are specified with the paramter @var{r}.
+## @strong{If you pass argument @var{P},
+## class switching is allowed}; however, you can not specify any external delay
+## (i.e., @var{Z} must be zero) and all servers must be fixed-rate or infinite-server nodes (@code{@var{m}(k) @leq{} 1} for all @math{k}).
+##
+## @item r
+## @code{@var{r}(c)} is the reference station for class @math{c}.
+## If omitted, station 1 is the reference station for all classes.
+## See @command{qncmvisits}.
+##
+## @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. This parameter can not be
+## used if you pass a routing matrix as the second parameter of
+## @code{qncmmva}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## If @math{k} is a FCFS, LCFS-PR or PS node (@code{@var{m}(k) @geq{}
+## 1}), then @code{@var{U}(c,k)} is the class @math{c} utilization at
+## center @math{k}, @math{0 @leq{} U(c,k) @leq{} 1}. 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)}. In this case the value of
+## @code{@var{U}(c,k)} may be greater than one.
+##
+## @item R
+## @code{@var{R}(c,k)} is the class @math{c} response time at
+## center @math{k}. The class @math{c} @emph{residence time}
+## at center @math{k} is @code{@var{R}(c,k) * @var{C}(c,k)}.
+## The total class @math{c} system response time
+## is @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} throughput can be computed
+## as @code{@var{X}(c,1) / @var{V}(c,1)}.
+##
+## @end table
+##
+## @quotation Note on numerical stability
+## In presence of load-dependent servers (e.g., if @code{@var{m}(i)>1}
+## for some @math{i}), the MVA algorithm is known to be numerically
+## unstable. Generally this problem manifests itself as negative
+## response times or utilizations. This is not a problem with the
+## @code{queueing} toolbox, but with the Mean Value Analysis algorithm,
+## and therefore has currently no easy workaround (aoart from using a
+## different solution technique, if available). This function prints a
+## warning if it detects numerical problems; you can disable the warning
+## with the command @code{warning("off", "qn:numerical-instability")}.
+## @end quotation
+##
+## @seealso{qnclosed, qncmmvaapprox, qncmvisits}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qncmmva( varargin )
+
+  if ( nargin < 2 || nargin > 5 )
+    print_usage();
+  endif
+
+  if ( nargin == 2 || ndims(varargin{3}) == 2 )
+    [err N S V m Z] = qncmchkparam( varargin{:} );
+    isempty(err) || error(err);
+    [U R Q X] = __qncmmva_nocs( N, S, V, m, Z );
+  else
+    [U R Q X] = __qncmmva_cs( varargin{:} );
+  endif
+
+endfunction
+
+##############################################################################
+## Analyze closed, multiclass QNs with class switching
+function [U R Q X] = __qncmmva_cs( N, S, P, r, m )
+  
+  if ( nargin < 3 || nargin > 5 )
+    print_usage();
+  endif
+
+  isvector(N) && all( N>=0 ) || ...
+      error( "N must be >=0" );
+  N = N(:)'; # make N a row vector
+  C = length(N); ## Number of classes
+  ( ndims(S) == 2 ) || ...
+      error( "S must be a matrix" );
+  K = columns(S); ## Number of service centers
+  size(S) == [C,K] || ...
+      error( "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] || ...
+      error( "P size mismatch (should be %dx%dx%dx%d)",C,K,C,K );
+
+  if ( nargin < 4 )
+    r = ones(1,C); # reference station
+  else
+
+    if (isscalar(r))
+      r = r*ones(1,C);
+    endif
+
+    ( isvector(r) && length(r) == C ) || ...
+	error("r must be a vector with %d elements", C);
+    r = r(:)';
+    all( r>=1 && r<=K ) || ...
+	error("elements of r are out of range [1,%d]",K);
+  endif
+
+  if ( nargin < 5 ) 
+    m = ones(1,K);
+  else
+    isvector(m) || ...
+        error( "m must be a vector" );
+    m = m(:)'; # make m a row vector
+    length(m) == K || ...
+        error( "m size mismatch (should be %d, is %d)", K, length(m) );
+  endif
+
+  ## Check consistency of parameters
+  all(S(:) >= 0) || ...
+      error( "S must be >= 0" );
+  all( any(S>0,2) ) || ...
+      error( "S must contain at least a value >0 for each row" );
+  all(P(:)>= 0) || ...
+      error( "P must be >=0" );
+
+  U = R = Q = X = zeros(C,K);
+
+  ## 1. Compute visit counts
+  [V ch] = qncmvisits(P,r);
+
+  ## 2. Identify chains
+  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] = __qncmmva_nocs( Nstar, Sstar, Vstar, m, zeros(size(Nstar)) );
+
+  ## 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) / max(1,m(k));
+    endfor
+  endfor
+
+  ## 9. Check for numerical instability
+  if ( any(U(:)<0) || any(R(:)<0) )
+    warning("qn:numerical-instability",
+	    "Numerical instability detected. Type 'help qncmmva' for details");
+  endif
+
+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] = __qncmmva_nocs( N, S, V, m, Z )
+
+  assert( nargin == 5 );
+
+  all( any(S>0,2) ) || ...
+      error( "S must contain at least a value >0 for each row" );
+
+  [C K] = size(S);
+
+  ## 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 __qncmmva_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))';
+
+      ## Update marginal probabilities for LD servers
+      for k=find(m>1)
+        j = 1:m(k)-1;
+	s = zeros(size(j));
+        for r=find(n_bar>0) # FIXME: 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 ./ max(1,repmat(m,C,1)); # U(c,k) = X(c)*D(c,k)
+  Q = diag(X)*(R.*V);
+  X = diag(X)*V;
+
+  ## Check for numerical instability
+  if ( any(U(:)<0) || any(R(:)<0) )
+    warning("qn:numerical-instability",
+	    "Numerical instability detected. Type 'help qncmmva' for details");
+  endif
+
+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( "qncmmva(N)" );
+%! fail( "qncmmva(N,S,V,m)", "independent" );
+%! S = [0 0 0; 1 1 1];
+%! fail( "qncmmva(N,S,V,m)", "must contain at least" );
+%! S = [1 2 3; 1 2 3];
+%! N = [1 1];
+%! V = zeros(3,2,3);
+%! fail( "qncmmva(N,S,V)", "size mismatch" );
+%! fail( "qncmmva([0.3 1], [1 2; 3 4])", "integer");
+%! fail( "qncmmva([-1 0], [1 2; 3 4])", "nonnegative");
+
+## Check degenerate case (population is zero); LI servers
+%!test
+%! S = [1 1 1; 1 1 1];
+%! N = [0 0];
+%! [U R Q X] = qncmmva(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] = qncmmva(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] = qncmmva(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(U(:)<=1) );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+## 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] = qncmmva( 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] = qncmmva(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) );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+## 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] = qncmmva(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 );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+## 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] = qncmmva(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(U(:)<=1) );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+## 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] = qncmmva(N,S,V);
+%! [U2 R2 Q2 X2] = qncsmva(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] = qncmmva(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 );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+## 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 = qncmvisits(P);
+%! [U R Q X] = qncmmva(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 );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+## 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 = qncmvisits(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] = qncmmva(N,S,V); # this invokes __qncmmva_nocs
+%! [U2 R2 Q2 X2] = qncmmva(N,S,P); # this invokes __qncmmva_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.
+%!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] = qncmmva(N,S,V,m);
+%! assert( U, [14.323 0 .358; 0 4.707 .157], 1e-3 );
+%! assert( R, [1.0 0 .047; 0 15 .934], 1e-3 );
+%! assert( Q, [14.323 0 .677; 0 4.707 .293], 1e-3 );
+%! assert( X, [14.323 0 14.323; 0 .314 .314], 1e-3 );
+
+## Same test as above, but using routing probabilities instead of
+## visits. Also, reordered the nodes such that 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;
+%! N = [15 5];
+%! m = [1 -1 -1];
+%! r = [1 1]; # reference station is station 1
+%! [U R Q X] = qncmmva(N,S,P,r,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 );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+## Example figure 9 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
+%! 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];
+%! [U R Q X] = qncmmva(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 );
+
+
+## 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] = qncmmva(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 );
+
+
+## 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] = qncmmva( 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");
+
+%!demo
+%! S = [1 0 .025; 0 15 .5];
+%! P = zeros(2,3,2,3);
+%! P(1,1,1,3) = P(1,3,1,1) = 1;
+%! P(2,2,2,3) = P(2,3,2,2) = 1;
+%! V = qncmvisits(P,[3 3]); # reference station is station 3
+%! N = [15 5];
+%! m = [-1 -1 1];
+%! [U R Q X] = qncmmva(N,S,V,m)
+
+
+## Example shown on Figure 9: 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.
+%!demo
+%! 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];
+%! [U R Q X] = qncmmva(N, S, P)
diff --git a/inst/qncmmvaap.m b/inst/qncmmvaap.m
new file mode 100644
index 0000000..af5fd60
--- /dev/null
+++ b/inst/qncmmvaap.m
@@ -0,0 +1,256 @@
+## 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}] =} qncmmvaap (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmvaap (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncmmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol}, @var{iter_max})
+##
+## @cindex Mean Value Analysys (MVA), approximate
+## @cindex MVA, approximate
+## @cindex closed network, multiple classes
+## @cindex multiclass network, closed
+##
+## 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
+## @tex
+## $$Q_i({\bf N}-{\bf 1}_c) \approx {n-1 \over n} Q_i({\bf N})$$
+## @end tex
+## @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{qncmmva}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qncmmvaap( N, S, V, m, Z, tol, iter_max )
+
+  if ( nargin < 3 || nargin > 7 )
+    print_usage();
+  endif
+
+  isvector(N) && all( N>=0 ) || ...
+      error( "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] || ...
+      error( "S size mismatch" );
+  size(V) == [C,K] || ...
+      error( "V size mismatch" );
+
+  if ( nargin < 4 || isempty(m) )
+    m = ones(1,K);
+  else
+    isvector(m) || ...
+	error( "m must be a vector");
+    m = m(:)'; # make m a row vector
+    ( length(m) == K && all( m <= 1 ) ) || ...
+        error( "m must be <= 1 and have %d elements", K );
+  endif
+
+  if ( nargin < 5 || isempty(Z) )
+    Z = zeros(1,C);
+  else
+    isvector(Z) || ...
+	error( "Z must be a vector" );
+    Z = Z(:)'; # make Z a row vector
+    ( length(Z) == C && all(Z >= 0 ) ) || ...
+	error( "Z must be >= 0 and have %d elements", C );
+  endif
+
+  if ( nargin < 6 || isempty(tol) )
+    tol = 1e-5;
+  endif
+
+  if ( nargin < 7 || isempty(iter_max) )
+    iter_max = 100;
+  endif
+
+  ## Check consistency of parameters
+  all(S(:) >= 0) || ...
+      error( "S contains negative values" );
+  all(V(:) >= 0) || ...
+      error( "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( "qncmmvaap(): 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( "qncmmvaap(N,S,V,m,Z)", "m must be" );
+%! m = [1 ; 1 ; 1];
+%! fail( "qncmmvaap(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] = qncmmvaap(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] = qncmmvaap(N,S,V,m);
diff --git a/inst/qncmnpop.m b/inst/qncmnpop.m
new file mode 100644
index 0000000..d020b3c
--- /dev/null
+++ b/inst/qncmnpop.m
@@ -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} =} qncmnpop (@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- at 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{qncmmva,qncmpopmix}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function H = qncmnpop( 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 = qncmnpop( [1 2 2] );
+%! assert( H, [1 1 0 0 0 0; 1 2 2 1 0 0; 1 3 5 5 3 1] );
diff --git a/inst/qncmpopmix.m b/inst/qncmpopmix.m
new file mode 100644
index 0000000..1fe8418
--- /dev/null
+++ b/inst/qncmpopmix.m
@@ -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 =} qncmpopmix (@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}(c)} requests of class @math{c}, 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}(c)} is the number of class @math{c} 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,c)} is the number of class @math{c} 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{qncmnpop}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function pop_mix = qncmpopmix( k, population )
+
+  if ( nargin != 2 ) 
+    print_usage();
+  endif
+
+  isvector( population ) && all( population>=0 ) || ...
+      error( "N must be an array >=0" );
+  R = length(population); # number of classes
+  ( isscalar(k) && k >= 0 && k <= sum(population) ) || ...
+      error( "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 = qncmpopmix( 1, N );
+%! assert( f, [1 0 0; 0 1 0; 0 0 1] );
+%! f = qncmpopmix( 2, N );
+%! assert( f, [2 0 0; 1 1 0; 0 2 0; 1 0 1; 0 1 1; 0 0 2] );
+%! f = qncmpopmix( 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 = qncmpopmix( 1, N );
+%! assert( f, [1 0; 0 1] );
+%! f = qncmpopmix( 2, N );
+%! assert( f,  [2 0; 1 1] );
+
diff --git a/inst/qncmva.m b/inst/qncmva.m
new file mode 100644
index 0000000..e1b531c
--- /dev/null
+++ b/inst/qncmva.m
@@ -0,0 +1,40 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncscmva} instead.
+##
+## @seealso{qncscmva}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qncmva( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "Function qncmva is deprecated. Please use qncscmva instead");
+  endif
+  [U R Q X] = qncscmva( varargin{:} );
+endfunction
diff --git a/inst/qncmvisits.m b/inst/qncmvisits.m
new file mode 100644
index 0000000..c9f0d46
--- /dev/null
+++ b/inst/qncmvisits.m
@@ -0,0 +1,339 @@
+## Copyright (C) 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}] =} qncmvisits (@var{P})
+## @deftypefnx {Function File} {[@var{V} @var{ch}] =} qncmvisits (@var{P}, @var{r})
+##
+## Compute the average number of visits to the service centers of a closed multiclass network with @math{K} service centers and @math{C} customer classes.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @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 allowed.
+##
+## @item r
+## @code{@var{r}(c)} is the index of class @math{c} reference station,
+## @math{r(c) \in @{1, @dots{}, K@}}, @math{c \in @{1, @dots{}, C@}}.
+## The class @math{c} visit count to server @code{@var{r}(c)}
+## (@code{@var{V}(c,r(c))}) is conventionally set to 1. The reference
+## station serves two purposes: (i) its throughput is assumed to be the
+## system throughput, and (ii) a job returning to the reference station
+## is assumed to have completed one cycle. Default is to consider
+## station 1 as the reference station for all classes.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item V
+## @code{@var{V}(c,i)} is the number of visits of class @math{c}
+## requests at center @math{i}.
+##
+## @item ch
+## @code{@var{ch}(c)} is the chain number that class @math{c} belongs
+## to. Different classes can belong to the same chain. Chains are
+## numbered sequentially starting from 1 (@math{1, 2, @dots{}}). The
+## total number of chains is @code{max(@var{ch})}.
+##
+## @end table
+## 
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [V chains] = qncmvisits( P, r )
+
+  if ( nargin < 1 || nargin > 2 )
+    print_usage();
+  endif
+
+  ndims(P) == 4 || ...
+      error("P must be a 4-dimensional matrix");
+
+  [C, K, C2, K2] = size( P );
+  (K == K2 && C == C2) || ...
+      error( "P must be a [C,K,C,K] matrix");
+
+  if ( nargin < 2)
+    r = ones(1,C);
+  else
+    isvector(r) && length(r) == C || ...
+	error("r must be a vector with %d elements",C);
+    all( r>=1 && r<=K ) || ...
+	error("elements in r must be in the range 1 - %d",K);
+    r = r(:)';
+  endif
+
+  ## 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,r(s)) = 1 for all s.
+  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); # CH(c,k) is the chain that class c at center k belongs to
+
+  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
+
+  constraints = zeros(1,nCH); # constraint(cc) = 1 iff we set a constraint for a class belonging to chain cc; we only set one constraint per chain
+
+  for c=1:C
+    cc = CH(c,r(c));
+    if ( cc == 0 || constraints(cc) == 0 ) 
+      ii = sub2ind([C K],c,r(c));
+      A(:,ii) = 0;
+      A(ii,ii) = 1;
+      if ( cc > 0 ) ## if r(c) is not an isolated node
+	constraints(cc) = 1;
+	b(ii) = 1;
+      endif
+    endif
+  endfor
+
+  V = reshape(b/A, C, K);
+  ## Make sure that no negative values appear (sometimes, numerical
+  ## errors produce tiny negative values instead of zeros)
+  V = max(0,V);
+endfunction
+
+%!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 = qncmvisits(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 = qncmvisits(P);
+%! for c=1:C
+%!   for i=1:K
+%!     assert(V(c,i), 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 = qncmvisits(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] = qncmvisits(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] = qncmvisits(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( "qncmvisits(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 = qncmvisits(P,[1,2]);
+%! assert( V, [1 0 1; 0 1 1], 1e-5 );
+
+%!test
+%! C = 2;
+%! K = 3;
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = 1;
+%! P(1,2,1,3) = 1;
+%! P(1,3,2,2) = 1;
+%! P(2,2,1,1) = 1;
+%! [V ch] = qncmvisits(P);
+%! assert( ch, [1 1] );
+
+## The following transition probability matrix is not well formed: note
+## that there is an outgoing transition from center 1, class 1 but not
+## incoming transition.
+%!test
+%! C = 2;
+%! K = 3;
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = 1;
+%! P(1,2,1,3) = 1;
+%! P(1,3,2,2) = 1;
+%! P(2,2,2,1) = 1;
+%! P(2,1,1,2) = 1;
+%! [V ch] = qncmvisits(P);
+%! assert( ch, [1 1] );
+
+## compute strongly connected components using Kosaraju's algorithm,
+## which requires two DFS visits. A better solution would be to use
+## Tarjan's algorithm.
+##
+## 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);
+      if (any(r))
+	s(r) = c++;
+      endif
+    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
+
diff --git a/inst/qnconvolution.m b/inst/qnconvolution.m
new file mode 100644
index 0000000..8c83a5d
--- /dev/null
+++ b/inst/qnconvolution.m
@@ -0,0 +1,40 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncsconv} instead.
+##
+## @seealso{qncsconv}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X G] = qnconvolution( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnconvolution is deprecated. Please use qncsconv instead");
+  endif
+  [U R Q X G] = qncsconv( varargin{:} );
+endfunction
diff --git a/inst/qnconvolutionld.m b/inst/qnconvolutionld.m
new file mode 100644
index 0000000..a6322d0
--- /dev/null
+++ b/inst/qnconvolutionld.m
@@ -0,0 +1,39 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncsconvld} instead.
+##
+## @seealso{qncsconvld}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X G] = qnconvolutionld( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnconvolutionld is deprecated. Please use qncsconvld instead");
+  endif
+  [U R Q X G] = qncsconvld( varargin{:} );
+endfunction
diff --git a/inst/qncsaba.m b/inst/qncsaba.m
new file mode 100644
index 0000000..6b96798
--- /dev/null
+++ b/inst/qncsaba.m
@@ -0,0 +1,131 @@
+## Copyright (C) 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}] =} qncsaba (@var{N}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsaba (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsaba (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsaba (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+##
+## @cindex bounds, asymptotic
+## @cindex asymptotic bounds
+## @cindex closed network, single class
+##
+## Compute Asymptotic Bounds for throughput and response time of closed, single-class networks.
+##
+## Single-server and infinite-server nodes are supported.
+## Multiple-server nodes and general load-dependent servers are not
+## supported.
+##
+## @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 at center @math{k}
+## (@code{@var{D}(k) @geq{} 0}).
+##
+## @item S
+## @code{@var{S}(k)} is the mean service time at center @math{k}
+## (@code{@var{S}(k) @geq{} 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{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 M/M/1-FCFS server.
+## This function does not support multiple-server nodes. Default
+## is 1.
+##
+## @item Z
+## External delay (@code{@var{Z} @geq{} 0}). Default is 0.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @itemx Xu
+## Lower and upper system throughput bounds.
+##
+## @item Rl
+## @itemx Ru
+## Lower and upper response time bounds.
+##
+## @end table
+##
+## @seealso{qncmaba}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xl Xu Rl Ru] = qncsaba( varargin ) #N, S, V, m, Z )
+  
+  if (nargin<2 || nargin>5)
+    print_usage();
+  endif
+
+  [err N S V m Z] = qncschkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  all(m<=1) || ...
+      error( "multiple server nodes are not supported" );
+
+  D = S.*V;
+
+  Dtot_single = sum(D(m==1)); # total demand at single-server nodes
+  Dtot_delay = sum(D(m<1)); # total demand at IS nodes
+  Dtot = sum(D); # total demand
+  Dmax = max(D); # max demand
+
+  Xl = N/(N*Dtot_single + Dtot_delay + Z);
+  Xu = min( N/(Dtot+Z), 1/Dmax );
+  Rl = max( Dtot, N*Dmax - Z );
+  Ru = N*Dtot_single + Dtot_delay;
+endfunction
+
+%!test
+%! fail("qncsaba(-1,0)", "N must be");
+%! fail("qncsaba(1,[])", "nonempty");
+%! fail("qncsaba(1,[-1 2])", "nonnegative");
+%! fail("qncsaba(1,[1 2],[1 2 3])", "incompatible size");
+%! fail("qncsaba(1,[1 2 3],[1 2 -1])", "nonnegative");
+%! fail("qncsaba(1,[1 2 3],[1 2 3],[1 2])", "incompatible size");
+%! fail("qncsaba(1,[1 2 3],[1 2 3],[1 2 1])", "not supported");
+%! fail("qncsaba(1,[1 2 3],[1 2 3],[1 1 1],-1)", "nonnegative");
+%! fail("qncsaba(1,[1 2 3],[1 2 3],1,[0 0])", "scalar");
+
+## Example 9.6 p. 913 Bolch et al.
+%!test
+%! N = 20;
+%! S = [ 4.6*2 8 ];
+%! Z = 120;
+%! [X_l X_u R_l R_u] = qncsaba(N, S, ones(size(S)), ones(size(S)), Z);
+%! assert( [X_u R_l], [0.109 64], 1e-3 );
+
diff --git a/inst/qncsbsb.m b/inst/qncsbsb.m
new file mode 100644
index 0000000..1561602
--- /dev/null
+++ b/inst/qncsbsb.m
@@ -0,0 +1,114 @@
+## 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}] =} qncsbsb (@var{N}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsbsb (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsbsb (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncsbsb (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+##
+## @cindex bounds, balanced system
+## @cindex closed network, single class
+## @cindex balanced system bounds
+##
+## Compute Balanced System Bounds on system throughput and response time for closed, single-class networks.
+##
+## @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{D}(k) @geq{} 0}).
+##
+## @item S
+## @code{@var{S}(k)} is the mean service time at center @math{k}
+## (@code{@var{S}(k) @geq{} 0}).
+##
+## @item V
+## @code{@var{V}(k)} is the average number of visits to center
+## @math{k} (@code{@var{V}(k) @geq{} 0}). Default is 1.
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center @math{k}. This
+## function supports @code{@var{m}(k) = 1} only (sing-eserver FCFS
+## nodes). This option is left for compatibility with
+## @code{qncsaba}, Default is 1.
+##
+## @item Z
+## External delay (@code{@var{Z} @geq{} 0}). Default is 0.
+##
+## @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{qncmbsb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xl Xu Rl Ru] = qncsbsb( varargin )
+
+  if (nargin<2 || nargin>5)
+    print_usage();
+  endif
+
+  [err N S V m Z] = qncschkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  all(m==1) || ...
+      error( "this function supports M/M/1 servers only" );
+
+  D = S .* V;
+
+  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("qncsbsb(-1,0)", "N must be");
+%! fail("qncsbsb(1,[])", "nonempty");
+%! fail("qncsbsb(1,[-1 2])", "nonnegative");
+%! fail("qncsbsb(1,[1 2],[1 2 3])", "incompatible size");
+%! fail("qncsbsb(1,[1 2 3],[1 2 3],[1 2])", "incompatible size");
+%! fail("qncsbsb(1,[1 2 3],[1 2 3],[1 2 1])", "M/M/1 servers");
+%! fail("qncsbsb(1,[1 2 3],[1 2 3],[1 1 1],-1)", "nonnegative");
+%! fail("qncsbsb(1,[1 2 3],[1 2 3],[1 1 1],[0 0])", "scalar");
+
diff --git a/inst/qncscmva.m b/inst/qncscmva.m
new file mode 100644
index 0000000..8c4eda3
--- /dev/null
+++ b/inst/qncscmva.m
@@ -0,0 +1,250 @@
+## 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}] =} qncscmva (@var{N}, @var{S}, @var{Sld}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncscmva (@var{N}, @var{S}, @var{Sld}, @var{V}, @var{Z})
+##
+## @cindex conditional MVA (CMVA)
+## @cindex Mean Value Analysis, conditional (CMVA)
+## @cindex closed network, single class
+## @cindex CMVA
+##
+## This is the implementation of the original Conditional MVA (CMVA)
+## algorithm, a numerically stable variant of MVA, as described in G.
+## Casale, @cite{A Note on Stable Flow-Equivalent Aggregation in Closed
+## Networks}. This function supports a network of @math{M @geq{} 1}
+## service centers and a single delay center. Servers @math{1, @dots{},
+## M-1} are load-independent; server @math{M} is load-dependent.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item N
+## 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
+## Vector of mean service times for load-independent (fixed rate) servers.
+## Specifically, @code{@var{S}(k)} is the mean service time on server
+## @math{k = 1, @dots{}, M-1} (@code{@var{S}(k) > 0}). If there are no
+## fixed-rate servers, then @code{S = []}
+##
+## @item Sld
+## @code{@var{Sld}(n)} is the inverse service rate at server @math{M}
+## (the load-dependent server) when there are @math{n} requests,
+## @math{n=1, @dots{}, N}. @code{@var{Sld}(n) = } @math{1 / \mu(n)}.
+##
+## @item V
+## @code{@var{V}(k)} is the average number of visits to service center
+## @math{k=1, @dots{}, M}, where @code{@var{V}(k) @geq{} 0}.
+## @code{@var{V}(1:M-1)} are the visit rates to the fixed rate servers;
+## @code{@var{V}(M)} is the visit rate to the load dependent server.
+##
+## @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} (@math{k=1, @dots{}, M})
+##
+## @item R
+## @code{@var{R}(k)} is the response time of center @math{k}, (@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}, (@math{k=1, @dots{}, M}).
+##
+## @item X
+## @code{@var{X}(k)} is the throughput of center @math{k}, (@math{k=1, @dots{}, M}).
+##
+## @end table
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qncscmva( N, S, Sld, V, Z )
+
+  ## This is a numerically stable implementation of the MVA algorithm,
+  ## described in G. Casale, A note on stable flow-equivalent aggregation in
+  ## closed networks. Queueing Syst. Theory Appl., 60:193–-202, December
+  ## 2008, http://dx.doi.org/10.1007/s11134-008-9093-6
+
+
+  if ( nargin < 4 || nargin > 5 )
+    print_usage();
+  endif
+
+  isscalar(N) && N >= 0 || ...
+      error("N must be a positive scalar");
+
+  (isempty(S) || isvector(S)) || ...
+      error("S must be a vector");
+  S = S(:)'; # make S a row vector
+  M = length(S)+1; # total number of service centers (excluding the delay center)
+  
+  isvector(Sld) && length(Sld) == N && all(Sld>=0) || ...
+      error("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) || ...
+      error("V must be a vector with %d elements", M);
+  V = V(:)'; # Make V a row vector
+
+  ## The reference paper assumes queue M (LD center) as reference.
+  ## Therefore, we need to rescale V
+
+  V(M) > 0 || ...
+      error("V(M) must be >0");
+
+  V = V / V(M);
+
+  if ( nargin == 5 )
+    isscalar(Z) && Z>=0 || ...
+	error("Z must be nonnegative");
+  else
+    Z = 0;
+  endif
+
+  if ( N == 0 )
+    U = R = Q = X = zeros(1,M);
+    return;
+  endif
+
+  ## Di(1+k) = service demand of server k=0,1,...,M-1 (server 0 is the delay center)
+  Di = zeros(1,M); 
+  Di(1) = Z;
+  Di(2:M) = S .* V(1:M-1);
+  ## DM(n,t), n=1, ..., N, t=1, ..., N
+  DM = zeros(N,N);
+
+  mu = 1./Sld; # rate function
+
+  ## Ri(1+k,:,:) = response time of server k=0,1,...,M
+  Ri = zeros(1+M,N,N); 
+
+  ## Qi(k,1+n,t) = queue length of server k=1,...,M, n=0,1,...,N, t=1,...,N+1
+  Qi = zeros(M,1+N,N+1);
+
+  Xs = zeros(N,N); # Xs = system throughput
+
+  ## Main MVA loop
+  for n=1:N 
+    for t=1:(N-n+1)
+      if ( n==1 )
+	DM(n,t) = 1/mu(t);
+      else # n>=2
+	DM(n,t) = Xs(n-1,t)/Xs(n-1,t+1)*DM(n-1,t);
+      endif
+
+      Ri(1+0,n,t) = Di(1+0);
+      i=1:M-1; Ri(1+i,n,t) = Di(1+i).*(1+Qi(i,1+n-1,t))';
+      Ri(1+M,n,t) = DM(n,t)*(1+Qi(M,1+n-1,t+1));
+
+      Xs(n,t) = n/sum(Ri(:,n,t));
+
+      i=1:M-1; Qi(i,1+n,t) = Di(1+i) .* Xs(n,t) .* (1+Qi(i,1+n-1,t))';
+      Qi(M,1+n,t) = DM(n,t) * Xs(n,t) * (1+Qi(M,1+n-1,t+1));
+    endfor
+  endfor
+  X = Xs(N,1).*V;
+  Q = Qi(1:M,1+N,1)';
+  ## Note that the result R is the *response time*, while the value
+  ## computed by the reference paper is the *residence time*. The
+  ## response time is equal to the residence time divided by the visit
+  ## ratios. FIXME: This will choke if the visit ratio is zero for some server k
+  R = Ri(2:M+1,N,1)' ./ V;
+  U = [Di(2:M) DM(N,1)] .* X ./ V;
+endfunction
+%!test
+%! N=5;
+%! S = [1 0.3 0.8 0.9];
+%! V = [1 1 1 1];
+%! [U1 R1 Q1 X1] = qncscmva( N, S(1:3), repmat(S(4),1,N), V );
+%! [U2 R2 Q2 X2] = qncsmva(N, S, V);
+%! assert( X1, X2, 1e-5 );
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 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] = qncscmva( N, S(1:3,1), S(4,:), V );
+%! [U2 R2 Q2 X2] = qncsmvald(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 2 1 1];
+%! Z = 3;
+%! [U1 R1 Q1 X1] = qncscmva( N, S(1:3,1), S(4,:), V, Z );
+%! [U2 R2 Q2 X2] = qncsmvald(N, S, V, Z);
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 1e-5 );
+%! assert( X1, X2, 1e-5 );
+
+%!demo
+%! maxN = 90; # Max population size
+%! Rmva = Rconv = Rcmva = zeros(1,maxN); # Results
+%! S = 4; Z = 10; m = 8;
+%! old = warning("query","qn:numerical-instability");
+%! warning("off","qn:numerical-instability");
+%! for N=1:maxN
+%!   [U R] = qncsmva(N,S,1,m,Z);		# Use MVA
+%!   Rmva(N) = R(1);
+%!   [U R] = qncsconv(N,[S Z],[1 1],[m -1]);	# Use Convolution
+%!   Rconv(N) = R(1);
+%!   if ( N > m )
+%!     Scmva = S ./ min(1:N,m);
+%!   else
+%!     Scmva = S ./ (1:N);
+%!   endif
+%!   [U R] = qncscmva(N,[],Scmva,1,Z);		# Use CMVA
+%!   Rcmva(N) = R(1);
+%! endfor
+%! warning(old.state,"qn:numerical-instability");
+%! plot(1:maxN, Rmva, ";MVA;", ...
+%!      1:maxN, Rconv, ";Convolution;", ...
+%!      1:maxN, Rcmva, ";CNVA;", "linewidth",2);
+%! xlabel("Population size (N)");
+%! ylabel("Response Time");
+%! ax=axis(); ax(3) = 0; ax(4) = 40; axis(ax);
+%! legend("location","northwest");
\ No newline at end of file
diff --git a/inst/qncsconv.m b/inst/qncsconv.m
new file mode 100644
index 0000000..89d1d9f
--- /dev/null
+++ b/inst/qncsconv.m
@@ -0,0 +1,226 @@
+## 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}] =} qncsconv (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qncsconv (@var{N}, @var{S}, @var{V}, @var{m})
+##
+## @cindex closed network, single class
+## @cindex normalization constant
+## @cindex convolution algorithm
+##
+## Analyze product-form, single class closed networks using the convolution algorithm.
+##
+## Load-independent service centers, multiple servers (@math{M/M/m}
+## queues) and IS nodes are supported. For general load-dependent
+## service centers, use @code{qncsconvld} 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{qncsconvld}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X G] = qncsconv( varargin )
+
+  if ( nargin < 3 || nargin > 4 )
+    print_usage();
+  endif
+
+  ## To be compliant with the reference, we use K to denote the
+  ## population size
+  [err K S V m] = qncschkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  N = length(S); # Number of service centers
+
+  ## This implementation is based on G. Bolch, S. Greiner, H. de Meer
+  ## and K. Trivedi, Queueing Networks and Markov Chains: Modeling and
+  ## Performance Evaluation with Computer Science Applications, Wiley,
+  ## 1998, pp. 313--317.
+
+  ## 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); 
+  F_n = zeros(N,K+1); F_n(:,1) = 1;
+  k=1:K; G_n(1) = 1; G_n(k+1) = F_n(1,k+1) = F(1,k,V,S,m); 
+  ## Main convolution loop
+  for n=2:N
+    G_nm1 = G_n;
+    k=1:K; F_n(n,1+k) = F(n,k,V,S,m);
+    # G_n(1) = 1;
+    G_n = conv( F_n(n,:), G_nm1(:) )(1:K+1);
+  endfor
+  ## Done computation of G(n,k).
+  G = G_n(:)'; # 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] = qncsconv( 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] = qncsmva(K, S, V);
+%! [U_con R_con Q_con X_con G_con] = qncsconv(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] = qncsmva(K, S, V, m);
+%! [U_con R_con Q_con X_con G] = qncsconv(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) || ...
+      error( "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] = qncsconv( 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
diff --git a/inst/qncsconvld.m b/inst/qncsconvld.m
new file mode 100644
index 0000000..ebe1c2b
--- /dev/null
+++ b/inst/qncsconvld.m
@@ -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}] =} qncsconvld (@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{qncsconv}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X G] = qncsconvld( N, S, V )
+
+  if ( nargin != 3 )
+    print_usage();
+  endif
+
+  ( isscalar(N) && N>0 ) || ...
+      error( "N must be a positive scalar" );
+  K = N; # To be compliant with the reference, we denote K as the population size
+  ( isvector(V) && all(V>=0) ) || ...
+      error( "V must be a vector >=0" );
+  V = V(:)'; # Make V a row vector
+  N = length(V); # Number of service centers
+  if ( isnumeric(S) ) 
+    ( rows(S) == N && columns(S) == K) || ...
+        error( sprintf("S size mismatch: is %dx%d, should be %dx%d", rows(S), columns(S),K,N ) );
+    all(S(:)>=0) || ...
+        error( "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=1:K
+    G_n(k+1) = F_n(1,k+1) = F(1,k,V,S);
+  endfor
+  ## Main convolution loop
+  for n=2:N
+    G_nm1 = G_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);
+  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( "qncsconvld(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] = qncsconvld( 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] = qncsmva(K, S, V);
+%! [U_con R_con Q_con X_con G] = qncsconvld(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] = qncsmva(K, S, V);
+%! [U_con R_con Q_con X_con G] = qncsconvld(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] = qncsconvld( 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
diff --git a/inst/qncsgb.m b/inst/qncsgb.m
new file mode 100644
index 0000000..4a49034
--- /dev/null
+++ b/inst/qncsgb.m
@@ -0,0 +1,250 @@
+## 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}, @var{Ql}, @var{Qu}] =} qncsgb (@var{N}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}, @var{Ql}, @var{Qu}] =} qncsgb (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}, @var{Ql}, @var{Qu}] =} qncsgb (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}, @var{Ql}, @var{Qu}] =} qncsgb (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+##
+## @cindex bounds, geometric
+## @cindex geometric bounds
+## @cindex closed network
+##
+## Compute Geometric Bounds (GB) on system throughput, system response time and server queue lenghts for closed, single-class 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 S
+## @code{@var{S}(k)} is the mean service time at center @math{k}
+## (@code{@var{S}(k) @geq{} 0}).
+##
+## @item V
+## @code{@var{V}(k)} is the visit ratio to center @math{k}
+## (@code{@var{V}(k) @geq{} 0}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center @math{k}.
+## This function only supports @math{M/M/1} queues, therefore
+## @var{m} must be @code{ones(size(S))}. 
+##
+## @item Z
+## external delay (think time, @code{@var{Z} @geq{} 0}). Default 0.
+##
+## @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 Rl
+## @itemx Ru
+## Lower and upper bound on the system response time. These bounds
+## are derived from @var{Xl} and @var{Xu} using Little's Law:
+## @code{@var{Rl} = @var{N} / @var{Xu} - @var{Z}}, 
+## @code{@var{Ru} = @var{N} / @var{Xl} - @var{Z}}
+##
+## @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
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_lower X_upper R_upper R_lower Q_lower Q_upper] = qncsgb( varargin ) 
+
+  ## This implementation is based on the paper: G.Casale, R.R.Muntz,
+  ## G.Serazzi. Geometric Bounds: a Noniterative Analysis Technique for
+  ## Closed Queueing Networks IEEE Transactions on Computers,
+  ## 57(6):780-794, Jun 2008.
+  ## http://doi.ieeecomputersociety.org/10.1109/TC.2008.37
+
+  ## 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 && nargin != 7 ) )
+    print_usage();
+  endif
+
+  [err N S V m Z] = qncschkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  ## This function requires N>0
+  N > 0 || ...
+      error( "N must be >0" );
+
+  all(m==1) || ...
+      error("this function only supports single server nodes");
+
+  L = S .* V;
+
+  L_tot = sum(L);
+  L_max = max(L);
+  M = length(L);
+  if ( nargin < 6 ) 
+    [X_minus X_plus] = qncsaba(N,L,ones(size(L)),m,Z);
+  else
+    X_minus = varargin{6};
+    X_plus = varargin{7};
+  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
+  R_lower = N / X_upper - Z;
+  R_upper = N / X_lower - Z;
+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) || error( "X_plus must be a scalar" );
+  isscalar(X_minus) || error( "X_minus must be a scalar" );
+  ( isscalar(N) && (N>=0) ) || error( "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( L<L_max ))); # Eq. (8)
+  Q_upper(i) = 1/m_max*(N-Z*X_minus - sum( Q_lower( L<L_max ))); # Eq. (13)
+endfunction
+
+%!test
+%! fail( "qncsgb( 1, [] )", "vector" );
+%! fail( "qncsgb( 1, [0 -1])", "nonnegative" );
+%! fail( "qncsgb( 0, [1 2] )", ">0" );
+%! fail( "qncsgb( -1, [1 2])", "nonnegative" );
+%! fail( "qncsgb( 1, [1 2],1,[1 -1])", "single server" );
+
+%!# 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] = qncsgb(N,D,1,1,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 = qncsvisits(P);
+%! Z = 2;
+%! Nmax = 20;
+%! tol = 1e-5; # compensate for numerical errors
+%! ## Test case with Z>0
+%! for n=1:Nmax
+%!   [X_gb_lower X_gb_upper NC NC Q_gb_lower Q_gb_upper] = qncsgb(n, S.*V, 1, 1, Z);
+%!   [U R Q X] = qnclosed( n, S, V, m, Z );
+%!   X_mva = X(1)/V(1);
+%!   assert( X_gb_lower <= X_mva+tol );
+%!   assert( X_gb_upper >= X_mva-tol );
+%!   assert( Q_gb_lower <= Q+tol ); # compensate for numerical errors
+%!   assert( Q_gb_upper >= Q-tol ); # compensate for numerical errors
+%! endfor
+
+%!test
+%! P = [0 0.3 0.7; 1 0 0; 1 0 0];
+%! S = [1 0.6 0.2];
+%! V = qncsvisits(P);
+%! Nmax = 20;
+%! tol = 1e-5; # compensate for numerical errors
+%!
+%! ## Test case with Z=0
+%! for n=1:Nmax
+%!   [X_gb_lower X_gb_upper NC NC Q_gb_lower Q_gb_upper] = qncsgb(n, S.*V);
+%!   [U R Q X] = qnclosed( n, S, V );
+%!   X_mva = X(1)/V(1);
+%!   assert( X_gb_lower <= X_mva+tol );
+%!   assert( X_gb_upper >= X_mva-tol );
+%!   assert( Q_gb_lower <= Q+tol );
+%!   assert( Q_gb_upper >= Q-tol );
+%! endfor
diff --git a/inst/qncsmva.m b/inst/qncsmva.m
new file mode 100644
index 0000000..8ced453
--- /dev/null
+++ b/inst/qncsmva.m
@@ -0,0 +1,349 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 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}] =} qncsmva (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qncsmva (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{G}] =} qncsmva (@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{qncsmvald} 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 at center @math{k}
+## (@code{@var{S}(k) @geq{} 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) @geq{}
+## 1}), then @code{@var{U}(k)} is the utilization of center @math{k},
+## @math{0 @leq{} U(k) @leq{} 1}. 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)}. In this case the
+## value of @code{@var{U}(k)} may be greater than one.
+##
+## @item R
+## @code{@var{R}(k)} is the response time at center @math{k}.
+## The @emph{Residence Time} at center @math{k} is
+## @code{@var{R}(k) * @var{V}(k)}.
+## The system response time @var{Rsys}
+## can be computed either as @code{@var{Rsys} = @var{N}/@var{Xsys} - Z}
+## or as @code{@var{Rsys} = dot(@var{R}, at var{V})}
+##
+## @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}- at 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
+##
+## @quotation Note on numerical stability
+## In presence of load-dependent servers (e.g., if @code{@var{m}(k)>1}
+## for some @math{k}), the MVA algorithm is known to be numerically
+## unstable. Generally this problem manifests itself as negative
+## response times or utilizations. This is not a problem with the
+## @code{queueing} toolbox, but with the Mean Value Analysis algorithm,
+## and therefore has currently no easy workaround (aoart from using a
+## different solution technique, if available). This function prints a
+## warning if it detects numerical problems; you can disable the warning
+## with the command @code{warning("off", "qn:numerical-instability")}.
+## @end quotation
+##
+## @seealso{qncsmvald,qncscmva}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X G] = qncsmva( varargin )
+
+  if ( nargin < 3 || nargin > 5 ) 
+    print_usage();
+  endif
+
+  [err N S V m Z] = qncschkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  K = length(S); # Number of servers
+
+  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
+
+  i_single = find( m==1 );
+  i_multi = find( m>1 );
+  i_delay = find( m<1 );
+
+  ## Initialize results
+  if ( length(i_multi)>0 )
+    p = zeros( K, max(m)+1 ); # p(i,j+1) is the probability that there are j jobs at server i
+    p(:,1) = 1;
+  endif
+  X_s = 0; # System throughput
+
+  ## 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);
+
+  if ( any(U<0) || any(R<0) )
+    warning("qn:numerical-instability",
+	    "Numerical instability detected. Type 'help qncsmva' for details");
+  endif
+    
+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] = __qncsmva_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( "qncsmva()", "Invalid" );
+%! fail( "qncsmva( 10, [1 2], [1 2 3] )", "incompatible size" );
+%! fail( "qncsmva( 10, [-1 1], [1 1] )", "nonnegative" );
+%! fail( "qncsmva( 10.3, [-1 1], [1 1] )", "integer" );
+%! fail( "qncsmva( -0.3, [-1 1], [1 1] )", "nonnegative" );
+
+## Check if networks with only one type of server are handled correctly
+%!test
+%! qncsmva(1,1,1,1);
+%! qncsmva(1,1,1,-1);
+%! qncsmva(1,1,1,2);
+%! qncsmva(1,[1 1],[1 1],[-1 -1]);
+%! qncsmva(1,[1 1],[1 1],[1 1]);
+%! qncsmva(1,[1 1],[1 1],[2 2]);
+
+## 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] = qncsmva(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] = qncsmva(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] = qncsmva(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 ) );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+%!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] = qncsmva(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 ) );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+%!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] = qncsmva(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 ) );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+%!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] = qncsmva(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] = qncsmva(n, S, V);
+%!   Xs = X(1)/V(1);
+%!   Rs = dot(R,V);
+%!   # Compare with balanced system bounds
+%!   [Xlbsb Xubsb Rlbsb Rubsb] = qncsbsb( n, S .* V );
+%!   assert( Xlbsb<=Xs );
+%!   assert( Xubsb>=Xs );
+%!   assert( Rlbsb<=Rs );
+%!   assert( Rubsb>=Rs );
+%!   # Compare with asymptotic bounds
+%!   [Xlab Xuab Rlab Ruab] = qncsaba( n, S .* V );
+%!   assert( Xlab<=Xs );
+%!   assert( Xuab>=Xs );
+%!   assert( Rlab<=Rs );
+%!   assert( Ruab>=Rs );
+%! endfor
+
+%!demo
+%! S = [ 0.125 0.3 0.2 ];
+%! V = [ 16 10 5 ];
+%! N = 20;
+%! m = ones(1,3);
+%! Z = 4;
+%! [U R Q X] = qncsmva(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 );
+
diff --git a/inst/qncsmvaap.m b/inst/qncsmvaap.m
new file mode 100644
index 0000000..a6f8fc6
--- /dev/null
+++ b/inst/qncsmvaap.m
@@ -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}] =} qncsmvaap (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvaap (@var{N}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvaap (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z}, @var{tol}, @var{iter_max})
+##
+## @cindex Mean Value Analysys (MVA), approximate
+## @cindex 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{qncsmvald} 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}- at 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{qncsmva,qncsmvald}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qncsmvaap( N, S, V, m, Z, tol, iter_max )
+
+  if ( nargin < 3 || nargin > 7 ) 
+    print_usage();
+  endif
+
+  isscalar(N) && N >= 0 || ...
+      error( "N must be >= 0" );
+  isvector(S) || ...
+      error( "S must be a vector" );
+  isvector(V) || ...
+      error( "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) || ...
+	error( "m must be a vector" );
+    m = m(:)'; # make m a row vector
+  endif
+
+  [err S V m] = common_size(S, V, m);
+  (err == 0) || ...
+      error( "S, V and m are of incompatible size" );
+  all(S>=0) || ...
+      error( "S must be a vector >= 0" );
+  all(V>=0) || ...
+      error( "V must be a vector >= 0" );
+  all(m<=1) || ...
+      error( "Vector m must be <= 1 (this function supports IS and single-server nodes only)" );
+
+  if ( nargin < 5 )
+    Z = 0;
+  else
+    (isscalar(Z) && Z >= 0) || ...
+        error( "Z must be >= 0" );
+  endif
+
+  if ( nargin < 6 )
+    tol = 1e-5;
+  else
+    ( isscalar(tol) && tol>0 ) || ...
+	error("tol must be a positive scalar");
+  endif
+
+  if ( nargin < 7 )
+    iter_max = 100;
+  else
+    ( isscalar(iter_max) && iter_max > 0 ) || ...
+	error("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( "qncsmvaap(): Convergence not reached after %d iterations", iter_max );
+  endif
+  X = Xs * V;
+  U = X .* S;  
+endfunction
+%!test
+%! fail( "qncsmvaap()", "Invalid" );
+%! fail( "qncsmvaap( 10, [1 2], [1 2 3] )", "S, V and m" );
+%! fail( "qncsmvaap( 10, [-1 1], [1 1] )", ">= 0" );
+%! fail( "qncsmvaap( 10, [1 2], [1 2], [1 2] )", "supports");
+%! fail( "qncsmvaap( 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] = qncsmvaap(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] = qncsmva(n,S,V,m,Z);
+%!   Xmva(n) = X(1)/V(1);
+%!   Rmva(n) = dot(R,V);
+%!   [U R Q X] = qncsmvaap(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");
diff --git a/inst/qncsmvablo.m b/inst/qncsmvablo.m
new file mode 100644
index 0000000..c82284c
--- /dev/null
+++ b/inst/qncsmvablo.m
@@ -0,0 +1,201 @@
+## 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}] =} qncsmvablo (@var{N}, @var{S}, @var{M}, @var{P} )
+##
+## @cindex queueing network with blocking
+## @cindex blocking queueing network
+## @cindex closed network, finite capacity
+## @cindex MVABLO
+##
+## Approximate MVA algorithm for closed 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}(k)} is the average service time 
+## requested on server @math{k} (@code{@var{S}(k) > 0}).
+##
+## @item M
+## @code{@var{M}(k)} is the capacity of center
+## @math{k}. The capacity is the maximum number of requests in a service
+## center, including the request currently in service (@code{@var{M}(k) @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}(k)} is the utilization of
+## service center @math{i}.
+##
+## @item R
+## @code{@var{R}(k)} is the average response time
+## of service center @math{i}.
+##
+## @item Q
+## @code{@var{Q}(k)} 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 <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qncsmvablo( 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 ) || ...
+      error( "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 ) || ...
+      error( "M must be a vector with %d elements", N );
+  all( M >= 1) || ...
+      error( "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) );
+
+  [na err] = dtmcchkP(P);
+  ( na>0 ) || ...
+      error( err );
+
+  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 = qncsvisits(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( "qncsmvablo( 10, [1 1], [4 5], [0 1; 1 0] )", "capacity");
+%! fail( "qncsmvablo( 6, [1 1], [4 5], [0 1; 1 1] )", "stochastic");
+%! fail( "qncsmvablo( 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]=qncsmvablo( 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]=qncsmvablo( K, S, M, P );
+%! assert( R, [6.925 8.061 4.185], 1e-3 );
+%! K = 12;
+%! [U R Q X]=qncsmvablo( 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] = qncsmvablo( K, S, M, P );
+%! assert( R, [1.674 5.007 7.639], 1e-3 );
+%! K = 12;
+%! [U R Q X] = qncsmvablo( 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] = qncsmvablo( K, S, M, P );
+%! V = qncsvisits(P);
+%! [U2 R2 Q2] = qncsmva( K, S, V );
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 1e-5 );
+
diff --git a/inst/qncsmvald.m b/inst/qncsmvald.m
new file mode 100644
index 0000000..34e1aaf
--- /dev/null
+++ b/inst/qncsmvald.m
@@ -0,0 +1,206 @@
+## 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}] =} qncsmvald (@var{N}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qncsmvald (@var{N}, @var{S}, @var{V}, @var{Z})
+##
+## @cindex Mean Value Analysys (MVA)
+## @cindex 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{qncsmva} 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
+##
+## @quotation Note
+## In presence of load-dependent servers,
+## the MVA algorithm is known to be numerically
+## unstable. Generally this problem manifests itself as negative
+## response times produced by this function.
+## @end quotation
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qncsmvald( N, S, V, Z )
+
+  if ( nargin < 3 || nargin > 4 ) 
+    print_usage();
+  endif
+
+  isvector(V) && all(V>=0) || ...
+      error( "V must be a vector >= 0" );
+  V = V(:)'; # make V a row vector
+  K = length(V); # Number of servers
+  isscalar(N) && N >= 0 || ...
+      error( "N must be >= 0" );
+  ( ismatrix(S) && rows(S) == K && columns(S) >= N ) || ...
+      error( "S size mismatch: is %dx%d, should be %dx%d", rows(S), columns(S), K, N );
+  all(S(:)>=0) || ...
+      error( "S must be >= 0" );
+
+  if ( nargin < 4 ) 
+    Z = 0;
+  else
+    isscalar(Z) && Z>=0 || ...
+        error( "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] = qncsmvald(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] = qncsmvald(N,Sld,V,Z);
+%! [U2 R2 Q2 X2] = qncsmva(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] = qncsmvald(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] = qncsmvald(N,Sld,V);
+%! [U2 R2 Q2 X2] = qncsmva(N,S,V,m);
+%! ## Note that qncsmvald computes the utilization in a different
+%! ## way as qncsmva; in fact, qncsmva knows that service
+%! ## center i has m(i)>1 servers, but qncsmvald 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 );
+
diff --git a/inst/qncspb.m b/inst/qncspb.m
new file mode 100644
index 0000000..bb08170
--- /dev/null
+++ b/inst/qncspb.m
@@ -0,0 +1,136 @@
+## 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}] =} qncspb (@var{N}, @var{D} )
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncspb (@var{N}, @var{S}, @var{V} )
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncspb (@var{N}, @var{S}, @var{V}, @var{m} )
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qncspb (@var{N}, @var{S}, @var{V}, @var{m}, @var{Z} )
+##
+## @cindex bounds, PB
+## @cindex PB bounds
+## @cindex closed network, single class
+##
+## Compute PB Bounds (C. H. Hsieh and S. Lam, 1987) for single-class,
+## closed networks.
+##
+## @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}
+## (@code{@var{D}(k) @geq{} 0}).
+##
+## @item S
+## @code{@var{S}(k)} is the mean service time at center @math{k}
+## (@code{@var{S}(k) @geq{} 0}).
+##
+## @item V
+## @code{@var{V}(k)} is the visit ratio to center @math{k}
+## (@code{@var{V}(k) @geq{} 0}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center @math{k}.
+## This function only supports @math{M/M/1} queues, therefore
+## @var{m} must be @code{ones(size(S))}. 
+##
+## @item Z
+## external delay (think time, @code{@var{Z} @geq{} 0}). Default 0.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @itemx Xu
+## Lower and upper bounds on the system throughput.
+##
+## @item Rl
+## @itemx Ru
+## Lower and upper bounds on the system response time.
+##
+## @end table
+##
+## @seealso{qncsaba, qbcsbsb, qncsgb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_lower X_upper R_lower R_upper] = qncspb( varargin )
+  if ( nargin < 2 || nargin > 5 )
+    print_usage();
+  endif
+
+  [err N S V m Z] = qncschkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  ( N>0 ) || ...
+      error("N must be positive");
+  all(m==1) || ...
+      error("qncspb only supports single server nodes");
+
+  D = S .* V;
+
+  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)
+  R_lower = N/X_upper-Z;
+  R_upper = N/X_lower-Z;
+endfunction
+
+%!test
+%! fail( "qncspb( 1, [] )", "vector" );
+%! fail( "qncspb( 1, [0 -1])", "nonnegative" );
+%! fail( "qncspb( 0, [1 2] )", "positive" );
+%! fail( "qncspb( -1, [1 2])", "nonnegative" );
+%! fail( "qncspb( 1, [1 2], [1,1], [2, 2])", "single server" );
+%! fail( "qncspb( 1, [1 2], [1,1], [1, 1], -1)", "nonnegative" );
+
+%!# shared test function
+%!function test_pb( D, expected, Z=0 )
+%! for i=1:rows(expected)
+%!   N = expected(i,1);
+%!   [X_lower X_upper] = qncspb(N,D,ones(size(D)),ones(size(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);
diff --git a/inst/qncsvisits.m b/inst/qncsvisits.m
new file mode 100644
index 0000000..27852ed
--- /dev/null
+++ b/inst/qncsvisits.m
@@ -0,0 +1,133 @@
+## Copyright (C) 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} =} qncsvisits (@var{P})
+## @deftypefnx {Function File} {@var{V} =} qncsvisits (@var{P}, @var{r})
+##
+## Compute the mean number of visits to the service centers of a
+## single class, closed network with @math{K} service centers.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @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 must be strongly connected, meaning that each node must be
+## reachable from every other node.
+##
+## @item r
+## Index of the reference station, @math{r \in @{1, @dots{}, K@}};
+## Default @code{@var{r}=1}. The traffic equations are solved by
+## imposing the condition @code{@var{V}(r) = 1}. A request returning to
+## the reference station completes its activity cycle.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item V
+## @code{@var{V}(i)} is the average number of visits to service center
+## @math{i}, assuming center @math{r} as the reference station.
+##
+## @end table
+## 
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function V = qncsvisits( P, r )
+
+  if ( nargin < 1 || nargin > 2 )
+    print_usage();
+  endif
+
+  issquare(P) || ...
+      error("P must be a square matrix");
+
+  [res err] = dtmcchkP(P);
+  (res>0) || ...
+      error( "invalid transition probability matrix P" );
+
+  K = rows(P);
+
+  if ( nargin < 2 )
+    r = 1;
+  else
+    isscalar(r) || ...
+	error("r must be a scalar");
+
+    (r>=1 && r<=K) || ...
+	error("r must be an integer in the range 1 - %d",K);
+  endif
+
+  V = zeros(size(P));
+  A = P-eye(K);
+  b = zeros(1,K);
+  A(:,r) = 0; A(r,r) = 1;
+  b(r) = 1;
+  V = b/A;
+  ## Make sure that no negative values appear (sometimes, numerical
+  ## errors produce tiny negative values instead of zeros)
+  V = max(0,V);
+endfunction
+%!test
+%! P = [-1 0; 0 0];
+%! fail( "qncsvisits(P)", "invalid" );
+%! P = [1 0; 0.5 0];
+%! fail( "qncsvisits(P)", "invalid" );
+%! P = [1 2 3; 1 2 3];
+%! fail( "qncsvisits(P)", "square" );
+%! P = [0 1; 1 0]; 
+%! fail( "qncsvisits(P,0)", "range" );
+%! fail( "qncsvisits(P,3)", "range" );
+
+%!test
+%!
+%! ## Closed, single class network
+%!
+%! P = [0 0.3 0.7; 1 0 0; 1 0 0];
+%! V = qncsvisits(P);
+%! assert( V*P,V,1e-5 );
+%! assert( V, [1 0.3 0.7], 1e-5 );
+
+%!test
+%!
+%! ## Test tolerance of the qncsvisits() function. 
+%! ## This test builds transition probability matrices and tries
+%! ## to compute the visit counts on them. 
+%!
+%! for k=[5, 10, 20, 50]
+%!   P = reshape(1:k^2, k, k);
+%!   P = P ./ repmat(sum(P,2),1,k);
+%!   V = qncsvisits(P);
+%!   assert( V*P, V, 1e-5 );
+%! endfor
+
+%!demo
+%! P = [0 0.3 0.7; ...
+%!      1 0   0  ; ...
+%!      1 0   0  ];
+%! V = qncsvisits(P)
+
diff --git a/inst/qnjackson.m b/inst/qnjackson.m
new file mode 100644
index 0000000..f8658ea
--- /dev/null
+++ b/inst/qnjackson.m
@@ -0,0 +1,116 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014 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})
+##
+## This function is deprecated. Please use @code{qnos} instead.
+##
+## @seealso{qnos}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U_or_pi R Q X] = qnjackson( lambda, S, P, m, k )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnjackson is deprecated. Please use qnos insgead");
+  endif
+  if ( nargin < 3 || nargin > 5 )
+    print_usage();
+  endif
+  ( isvector(lambda) && all(lambda>=0) ) || ...
+      error( "lambda must be a vector >= 0" );
+  lambda=lambda(:)'; # make lambda a row vector
+  N = length(lambda);
+  isvector(S) || ...
+      error( "S must be a vector" );
+  S = S(:)'; # make S a row vector
+  size_equal(lambda,S) || ...
+      error( "lambda and S must of be of the same length" );
+  all(S>0) || ...
+      error( "S must be >0" );
+  [N,N] == size(P) || ...
+      error(" 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 || isempty(m) )
+    m = ones(1,N);
+  else
+    [errorcode, lambda, m] = common_size(lambda, m);
+    ( isvector(m) && (errorcode==0) ) || ...
+        error("m and lambda must have the same length" );
+  endif
+
+  ## Compute the arrival rates using the traffic equation:
+  l = sum(lambda)*qnosvisits( 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) ) || ...
+        error( "k must be a vector of the same size as lambda" );
+    all(k>=0) || ...
+        error( "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
diff --git a/inst/qnmarkov.m b/inst/qnmarkov.m
new file mode 100644
index 0000000..8b2907e
--- /dev/null
+++ b/inst/qnmarkov.m
@@ -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{k} has @math{m_k > 0} servers, and has a total (finite) capacity of
+## @math{C_k \geq m_k} which includes both buffer space and servers.
+## The buffer space at service center @math{k} is therefore
+## @math{C_k - m_k}.
+##
+## @item The network can be open, with external arrival rate to
+## center @math{k} equal to 
+## @math{\lambda_k}, 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_{i, j}} 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_{i, j}}.
+##
+## @item Blocking type is Repetitive-Service (RS). Service
+## center @math{j} is @emph{saturated} if the number of requests is equal
+## to its capacity @math{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}(k) @geq{} 0} to service center
+## @math{k} 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}(k)} is the average service time at service center
+## @math{k}
+##
+## @item C
+## @code{@var{C}(k)} is the Capacity of service center @math{k}. The capacity includes both
+## the buffer and server space @code{@var{m}(i)}. Thus the buffer space is
+## @code{@var{C}(k)- at var{m}(k)}.
+##
+## @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}(k)} is the number of servers at service center
+## @math{k}. Note that @code{@var{m}(k) @geq{} @var{C}(k)} for each @var{k}.
+## If @var{m} is omitted, all service centers are assumed to have a
+## single server (@code{@var{m}(k) = 1} for all @math{k}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## @code{@var{U}(k)} is the utilization of 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 customers in the
+## service center @math{k}, @emph{including} the request in service.
+##
+## @item X
+## @code{@var{X}(k)} is the throughput of service center @math{k}.
+##
+## @end table
+##
+## @quotation Note
+##
+## The space complexity of this implementation is
+## @math{O( \prod_{k=1}^K (C_k + 1)^2)}. The time complexity is dominated
+## by the time needed to solve a linear system with 
+## @math{\prod_{k=1}^K (C_k + 1)}
+## unknowns.
+##
+## @end quotation
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)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) || error( "S must be a vector" );
+  K = length(S); # number of service centers
+
+  if ( nargin < 5 )
+    m = ones(1,K);
+  else
+    size_equal(m,S) || error( "m must have the same langth as S" );
+  endif
+
+  ( [K,K] == size(P) && all( all(P>=0)) && all(sum(P,2) <= 1)) || ...
+      error( "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 ) || ...
+        error( "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 = qncmpopmix( 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 = qncmpopmix( 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, qncsvisits(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
+
diff --git a/inst/qnmg1.m b/inst/qnmg1.m
new file mode 100644
index 0000000..82c2f2b
--- /dev/null
+++ b/inst/qnmg1.m
@@ -0,0 +1,39 @@
+## Copyright (C) 2009 Dmitry Kolesnikov
+## Copyright (C) 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}] =} qnmg1 (@var{lambda}, @var{xavg}, @var{x2nd})
+##
+## This function is deprecated. Please use @code{qsmg1} instead.
+##
+## @seealso{qsmg1}
+##
+## @end deftypefn
+
+## Author: Dmitry Kolesnikov
+
+function [U R Q X p0] = qnmg1( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnmg1 is deprecated. Please use qsmg1 instead");
+  endif
+  [U R Q X p0] = qsmg1( varargin{:} );
+endfunction
diff --git a/inst/qnmh1.m b/inst/qnmh1.m
new file mode 100644
index 0000000..389e84c
--- /dev/null
+++ b/inst/qnmh1.m
@@ -0,0 +1,39 @@
+## Copyright (C) 2009 Dmitry Kolesnikov
+## Copyright (C) 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}] =} qnmh1 (@var{lambda}, @var{mu}, @var{alpha})
+##
+## This function is deprecated. Please use @code{qsmh1} instead.
+##
+## @seealso{qsmh1}
+##
+## @end deftypefn
+
+## Author: Dmitry Kolesnikov
+
+function [U R Q X p0] = qnmh1( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnmh1 is deprecated. Please use qsmh1 instead");
+  endif
+  [U R Q S p0] = qsmh1( varargin{:} );
+endfunction
diff --git a/inst/qnmix.m b/inst/qnmix.m
new file mode 100644
index 0000000..07719f3
--- /dev/null
+++ b/inst/qnmix.m
@@ -0,0 +1,245 @@
+## 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{qnom} or @code{qncmmva}
+## 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 In other words, for each class @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
+##
+## @item S
+## @code{@var{S}(c,k)} is the mean class @math{c} service time at 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 center @math{k} (@code{@var{V}(c,k) @geq{} 0}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at 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 center
+## is assumed to be of type @math{M/M/1}-FCFS. 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 class @math{c} utilization at center @math{k}.
+##
+## @item R
+## @code{@var{R}(c,k)} is class @math{c} response time at center @math{k}.
+##
+## @item Q
+## @code{@var{Q}(c,k)} is the average number of
+## class @math{c} requests at center @math{k}.
+##
+## @item X
+## @code{@var{X}(c,k)} is class @math{c} throughput at center @math{k}.
+##
+## @end table
+##
+## @seealso{qncmmva, qncm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)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) || ...
+      error( "lambda must be a vector" );
+  lambda = lambda(:)';
+  isvector(N) || ...
+      error( "N must be a vector" );
+  N = N(:)';
+  size_equal(lambda,N) || ...
+      error( "lambda and N must be of equal length" );
+  ( !any( lambda>0 & N>0 ) ) || ...
+      error("A class cannot be open and closed at the same time. Check lambda and N" );
+  ( all( lambda>0 | N>0 ) ) || ...
+      error( "A class cannot be neither open nor closed. Check lambda and N" );
+  size_equal(S,V) || ...
+      error( "S and V must have the same size" );
+  C = length(lambda); # number of classes
+  K = columns(S); # number of service centers
+  rows(S) == C || ...
+      error( "S must have %d rows", C );
+  if ( nargin < 5 ) 
+    m = ones(1,K);
+  else
+    isvector( m ) || ...
+        error( "m must be a vector" );
+    m = m(:)';
+    size_equal(lambda,m) || ...
+        error( "lambda and m must be of equal length" );
+  endif
+  all( m<=1 ) || ...
+      error( "This function supports single-server and delay centers only. Check m" );
+  if ( !any(lambda>0) ) 
+    warning( "qnmix(): There are no open classes. Using qncmmva()" );
+    [U R Q X] = qncmmva( N, S, V, m );
+    return;
+  endif
+  if ( !any(N>0) ) 
+    warning( "qnmix(): There are no closed classes. Using qnom()" );
+    [U R Q X] = qnom( 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] = qncmmva(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)
+  ## The following is needed to avoid division by zero
+  idx = false(size(X));
+  idx(op,:) = ( S(op,:)>0 & V(op,:)>0 );
+  X(idx) = U(idx) ./ S(idx);
+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 );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+%!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 ); #FIXME
+%! 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 );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+%!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 );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
diff --git a/inst/qnmknode.m b/inst/qnmknode.m
new file mode 100644
index 0000000..845047d
--- /dev/null
+++ b/inst/qnmknode.m
@@ -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{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
+## Mean service time.
+##
+## @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 <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function Q = qnmknode( node, S, varargin )
+
+  ischar(node) || ...
+      error( "Parameter \"node\" must be a string" );
+
+  node = tolower(node);
+
+  isvector(S) || ismatrix(S) || ...
+      error( "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};
+    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};
+    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
+    error( "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
+  ( isnumeric(m) && m>=1 ) || ...
+      error("m must be >=1");
+  ( isnumeric(s2) && s2>= 0 ) || ...
+      error("s2 must be >=0");
+  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" );
diff --git a/inst/qnmm1.m b/inst/qnmm1.m
new file mode 100644
index 0000000..20ccf6e
--- /dev/null
+++ b/inst/qnmm1.m
@@ -0,0 +1,39 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qsmm1} instead.
+##
+## @seealso{qsmm1}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0] = qnmm1( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnmm1 is deprecated. Please use qsmm1 instead");
+  endif
+  [U R Q X p0] = qsmm1( varargin{:} );
+endfunction
diff --git a/inst/qnmm1k.m b/inst/qnmm1k.m
new file mode 100644
index 0000000..03b7e8c
--- /dev/null
+++ b/inst/qnmm1k.m
@@ -0,0 +1,60 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Use @code{qsmm1k} instead.
+##
+## @seealso{qsmm1k}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pK] = qnmm1k( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnmm1k is deprecated. Please use qsmm1k instead");
+  endif
+  [U R Q X p0 pK] = qsmm1k( varargin{:} );
+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(ctmc_bd( birth, death ));
+%! U2 = 1-q(1);
+%! Q2 = dot( [0:K], q );
+%! assert( U1, U2, 1e-4 );
+%! assert( Q1, Q2, 1e-4 );
+
diff --git a/inst/qnmminf.m b/inst/qnmminf.m
new file mode 100644
index 0000000..5c5a5eb
--- /dev/null
+++ b/inst/qnmminf.m
@@ -0,0 +1,42 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qsmminf} instead.
+##
+## @seealso{qsmminf}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0] = qnmminf( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnmminf is deprecated. Please use qsmminf instead");
+  endif
+  [U R Q X p0] = qsmminf( varargin{:} );
+endfunction
+%!test
+%! fail( "qnmminf( [1 2], [1 2 3] )", "incompatible size");
+%! fail( "qnmminf( [-1 -1], [1 1] )", ">0" );
diff --git a/inst/qnmmm.m b/inst/qnmmm.m
new file mode 100644
index 0000000..4f6ae91
--- /dev/null
+++ b/inst/qnmmm.m
@@ -0,0 +1,40 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qsmmm} instead.
+##
+## @seealso{qsmmm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pm] = qnmmm( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnmmm is deprecated. Please use qsmmm instead");
+  endif
+  [U R Q X p0 pm] = qsmmm( varargin{:} );
+endfunction
diff --git a/inst/qnmmmk.m b/inst/qnmmmk.m
new file mode 100644
index 0000000..1e4cb65
--- /dev/null
+++ b/inst/qnmmmk.m
@@ -0,0 +1,115 @@
+## Copyright (C) 2012 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox is free software: you can redistribute it and/or
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## 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})
+##
+## This function is deprecated. Please use @code{qsmmmk} instead.
+##
+## @seealso{qsmmmk}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pK] = qnmmmk( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnmmmk is deprecated. Please use qsmmmk instead");
+  endif
+  [U R Q X p0 pK] = qsmmmk( varargin{:} );
+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(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
+
diff --git a/inst/qnmvablo.m b/inst/qnmvablo.m
new file mode 100644
index 0000000..99c292c
--- /dev/null
+++ b/inst/qnmvablo.m
@@ -0,0 +1,90 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncsmvablo} instead.
+##
+## @seealso{qncsmvablo}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnmvablo( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnmvablo is deprecated. Please use qncsmvablo instead");
+  endif
+  [U R Q X] = qncsmvablo( varargin{:} );
+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 = qncsvisits(P);
+%! [U2 R2 Q2] = qnclosedsinglemva( K, S, V );
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 1e-5 );
+
diff --git a/inst/qnmvapop.m b/inst/qnmvapop.m
new file mode 100644
index 0000000..151c6e9
--- /dev/null
+++ b/inst/qnmvapop.m
@@ -0,0 +1,39 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qncmnpop} instead.
+##
+## @seealso{qncmnpop}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function H = qnmvapop( N )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnmvapop is deprecated. Please use qncmnpop instead");
+  endif
+  H = qncmnpop( N );
+endfunction
diff --git a/inst/qnom.m b/inst/qnom.m
new file mode 100644
index 0000000..e3af368
--- /dev/null
+++ b/inst/qnom.m
@@ -0,0 +1,307 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 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}] =} qnom (@var{lambda}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnom (@var{lambda}, @var{S}, @var{V}, @var{m})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnom (@var{lambda}, @var{S}, @var{P})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnom (@var{lambda}, @var{S}, @var{P}, @var{m})
+##
+## @cindex open network, multiple classes
+## @cindex multiclass network, open
+##
+## 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.
+##
+## @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 servers or delay
+## centers: multiple-server and general load-dependent centers are not
+## supported.
+## Note that the meaning of parameter @var{lambda} is different 
+## from one case to the other (see below).
+## @end quotation
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## If this function is invoked as @code{qnom(lambda, S, V, @dots{})},
+## then @code{@var{lambda}(c)} is the external arrival rate of class
+## @math{c} customers (@code{@var{lambda}(c) @geq{} 0}). If this
+## function is invoked as @code{qnom(lambda, S, P, @dots{})}, then
+## @code{@var{lambda}(c,k)} is the external arrival rate of class
+## @math{c} customers at center @math{k} (@code{@var{lambda}(c,k) @geq{}
+## 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, mean service times must be class-independent.
+##
+## @item V
+## @code{@var{V}(c,k)} is the visit ratio of class @math{c}
+## customers to service center @math{k} (@code{@var{V}(c,k) @geq{} 0 }).
+## @strong{If you pass this argument, 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 argument @var{P},
+## class switching is allowed}; however, all servers must be fixed-rate or infinite-server nodes (@code{@var{m}(k) @leq{} 1} for all @math{k}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center @math{i}. If
+## @code{@var{m}(k) < 1}, enter @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 all @math{k}.
+##
+## @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,qnos,qnomvisits}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+function [U R Q X] = qnom( varargin )
+  if ( nargin < 2 || nargin > 4 )
+    print_usage();
+  endif
+
+  if ( nargin == 2 || ndims(varargin{3}) == 2 )
+
+    [err lambda S V m] = qnomchkparam( varargin{:} );
+
+  else
+
+    lambda = varargin{1};
+    ( ndims(lambda) == 2 && all( lambda(:) >= 0 ) ) || ...
+	error( "lambda must be >= 0" );
+    [C,K] = size(lambda);
+    S = varargin{2};
+    ( ndims(S) == 2 && size(S) == [C,K] ) || ...
+	error( "S size mismatch (should be [%d,%d])", C, K );
+    P = varargin{3};
+    ( ndims(P) == 4 && size(P) == [C,K,C,K] ) || ...
+	error( "P size mismatch (should be %dx%dx%dx%d)",C,K,C,K );
+    
+    V = qnomvisits(P,lambda);
+
+    if ( nargin < 4 )
+      m = ones(1,K);
+    else
+      m = varargin{4};
+      isvector(m) || ...
+          error( "m must be a vector" );
+      m = m(:)'; # make m a row vector
+      length(m) == K || ...
+          error( "m size mismatch (should be %d, is %d)", K, length(m) );
+      all(m<=1) || ...
+	  error( "IF you use parameter P, m must be <= 1");
+    endif
+
+    lambda = sum(lambda,2); # lambda(c) is the overall class c arrival rate
+  endif
+
+  [C K] = size(S);
+
+  U = R = Q = X = zeros(C,K);
+
+  ## NOTE; Zahorjan et al. define the class c throughput at center k as
+  ## X(c,k) = lambda(c) * V(c,k). However, this assumes a definition of
+  ## V(c,k) that is different from what is returned by the qnomvisits()
+  ## function. The queueing package defines V(c,k) as the class c visit
+  ## _ratio_ at center k (see the documentation of the queueing package
+  ## to see the formal definition of V(c,k) as the solution of a linear
+  ## system of equations), while Zahorjan et al. define V(c,k) as the
+  ## _number of visits_ at center k. If you want to try the examples
+  ## on Zahorjan with this function, you need to scale V(c,k)
+  ## as lambda / lambda(c) * V(c,k).
+
+  X = sum(lambda)*V; # X(c,k) = lambda*V(c,k);
+
+  ## If there are M/M/k servers with k>=1, compute the maximum
+  ## processing capacity
+  m(m<1) = -1; # avoid division by zero in next line
+  rho = X .* S * diag( 1 ./ m ); # rho(c,k) = X(c,k) * S(x,k) / m(k)
+  [Umax kmax] = max( sum(rho,1) );
+  (Umax < 1) || ...
+      error( "Processing capacity exceeded at center %d", kmax );
+
+  ## Compute utilizations (for IS nodes compute also response time and
+  ## queue lenghts)
+  for k=1:K
+    for c=1:C
+      if ( m(k) > 1 ) # M/M/m-FCFS
+	[U(c,k)] = qsmmm( X(c,k), 1/S(c,k), m(k) );
+      elseif ( m(k) == 1 ) # M/M/1 or -/G/1-PS
+	[U(c,k)] = qsmm1( X(c,k), 1/S(c,k) );
+      else # -/G/inf
+  	[U(c,k) R(c,k) Q(c,k)] = qsmminf( X(c,k), 1/S(c,k) );
+      endif
+    endfor
+  endfor
+  assert( sum(U,1) < 1 ); # sanity check
+
+  ## Adjust response times and queue lengths for FCFS queues
+  k_fcfs = find(m>=1);
+  for c=1:C
+    Q(c,k_fcfs) = U(c,k_fcfs) ./ ( 1 - sum(U(:,k_fcfs),1) );
+    R(c,k_fcfs) = Q(c,k_fcfs) ./ X(c,k_fcfs); # Use Little's law
+  endfor
+
+endfunction
+%!test
+%! fail( "qnom([1 1], [.9; 1.0])", "exceeded at center 1");
+%! fail( "qnom([1 1], [0.9 .9; 0.9 1.0])", "exceeded at center 2");
+%! #qnom([1 1], [.9; 1.0],[],2); # should not fail, M/M/2-FCFS
+%! #qnom([1 1], [.9; 1.0],[],-1); # should not fail, -/G/1-PS
+%! fail( "qnom(1./[2 3], [1.9 1.9 0.9; 2.9 3.0 2.9])", "exceeded at center 2");
+%! #qnom(1./[2 3], [1 1.9 0.9; 0.3 3.0 1.5],[],[1 2 1]); # should not fail
+
+%!test
+%! V = [1 1; 1 1];
+%! S = [1 3; 2 4];
+%! lambda = [3/19 2/19];
+%! [U R Q X] = qnom(lambda, S, diag( lambda / sum(lambda) ) * 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 );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+%!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] = qnom(lambda, S, diag( lambda / sum(lambda) ) * 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 );
+%! assert( Q, R.*X, 1e-5 ); # Little's Law
+
+%!test
+%! # example 7.7 p. 304 Bolch et al. Please note that the book uses the 
+%! # notation P(i,r,j,s) (i,j are service centers, r,s are job
+%! # classes) while the queueing package uses P(r,i,s,j)
+%! P = zeros(2,3,2,3);
+%! lambda = S = zeros(2,3);
+%! P(1,1,1,2) = 0.4;
+%! P(1,1,1,3) = 0.3;
+%! P(1,2,1,1) = 0.6;
+%! P(1,2,1,3) = 0.4;
+%! P(1,3,1,1) = 0.5;
+%! P(1,3,1,2) = 0.5;
+%! P(2,1,2,2) = 0.3;
+%! P(2,1,2,3) = 0.6;
+%! P(2,2,2,1) = 0.7;
+%! P(2,2,2,3) = 0.3;
+%! P(2,3,2,1) = 0.4;
+%! P(2,3,2,2) = 0.6;
+%! S(1,1) = 1/8;
+%! S(1,2) = 1/12;
+%! S(1,3) = 1/16;
+%! S(2,1) = 1/24;
+%! S(2,2) = 1/32;
+%! S(2,3) = 1/36;
+%! lambda(1,1) = lambda(2,1) = 1;
+%! V = qnomvisits(P,lambda);
+%! assert( V, [ 3.333 2.292 1.917; 10 8.049 8.415] ./ 2, 1e-3);
+%! [U R Q X] = qnom(sum(lambda,2), S, V);
+%! assert( sum(U,1), [0.833 0.442 0.354], 1e-3 );
+%! # Note: the value of K_22 (corresponding to Q(2,2)) reported in the book
+%! # is 0.5. However, hand computation using the exact same formulas
+%! # from the book produces a different value, 0.451
+%! assert( Q, [2.5 0.342 0.186; 2.5 0.451 0.362], 1e-3 );
+
+## Check that the results of qnom_nocs and qnom_cs are the same
+## for multiclass networks WITHOUT class switching.
+%!test
+%! P = zeros(2,2,2,2);
+%! P(1,1,1,2) = 0.8; P(1,2,1,1) = 1;
+%! P(2,1,2,2) = 0.9; P(2,2,2,1) = 1;
+%! S = zeros(2,2);
+%! S(1,1) = 1.5; S(1,2) = 1.2;
+%! S(2,1) = 0.8; S(2,2) = 2.5;
+%! lambda = zeros(2,2);
+%! lambda(1,1) = 1/20;
+%! lambda(2,1) = 1/30;
+%! [U1 R1 Q1 X1] = qnom(lambda, S, P); # qnom_cs
+%! [U2 R2 Q2 X2] = qnom(sum(lambda,2), S, qnomvisits(P,lambda)); # qnom_nocs
+%! assert( U1, U2, 1e-5 );
+%! assert( R1, R2, 1e-5 );
+%! assert( Q1, Q2, 1e-5 );
+%! assert( X1, X2, 1e-5 );
+
+%!demo
+%! P = zeros(2,2,2,2);
+%! lambda = zeros(2,2);
+%! S = zeros(2,2);
+%! P(1,1,2,1) = P(1,2,2,1) = 0.2;
+%! P(1,1,2,2) = P(2,2,2,2) = 0.8;
+%! S(1,1) = S(1,2) = 0.1;
+%! S(2,1) = S(2,2) = 0.05;
+%! rr = 1:100;
+%! Xk = zeros(2,length(rr));
+%! for r=rr
+%!   lambda(1,1) = lambda(1,2) = 1/r;
+%!   [U R Q X] = qnom(lambda,S,P);
+%!   Xk(:,r) = sum(X,1)';
+%! endfor
+%! plot(rr,Xk(1,:),";Server 1;","linewidth",2, ...
+%!      rr,Xk(2,:),";Server 2;","linewidth",2);
+%! xlabel("Class 1 interarrival time");
+%! ylabel("Throughput");
+
diff --git a/inst/qnomaba.m b/inst/qnomaba.m
new file mode 100644
index 0000000..abad37b
--- /dev/null
+++ b/inst/qnomaba.m
@@ -0,0 +1,112 @@
+## Copyright (C) 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}] =} qnomaba (@var{lambda}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Rl}] =} qnomaba (@var{lambda}, @var{S}, @var{V})
+##
+## @cindex bounds, asymptotic
+## @cindex open network
+## @cindex multiclass network, open
+##
+## Compute Asymptotic Bounds for open, multiclass networks.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## @code{@var{lambda}(c)} is the class @math{c} arrival rate to the
+## system.
+##
+## @item D
+## @code{@var{D}(c, k)} is class @math{c} service demand 
+## at center @math{k}. (@code{@var{D}(c, k) @geq{} 0} for all
+## @math{k}).
+##
+## @item S
+## @code{@var{S}(c, k)} is the mean service time of class @math{c}
+## requests at center @math{k}. (@code{@var{S}(c, k) @geq{} 0} for all
+## @math{k}).
+##
+## @item V
+## @code{@var{V}(c, k)} is the mean number of visits of class @math{c}
+## requests at center @math{k}. (@code{@var{V}(c, k) @geq{} 0} for all
+## @math{k}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @item Xu
+## Per-class lower and upper throughput bounds. For example,
+## @code{@var{Xu}(c)} is the upper bound for class @math{c} throughput.
+## @code{Xl} is always @math{0} since there can be no lower bound
+## on the throughput of open networks.
+##
+## @item Rl
+## @item Ru
+## Per-class lower and upper response time bounds. 
+## @code{Ru} is always @code{+inf} since there can be no upper bound
+## on the response time of open networks.
+##
+## @end table
+##
+## @seealso{qnombsb}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_lower X_upper R_lower R_upper] = qnomaba( lambda, S, V )
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+  (isvector(lambda) && length(lambda)>0) || ...
+      error( "lambda must be a nonempty vector" );
+  all(lambda > 0) || ...
+      error( "lambda must contain nonnegative values" );
+  lambda = lambda(:)';
+  C = length(lambda);
+  ( ismatrix(S) && rows(S)==C ) || ...
+      error( "S/D must be a matrix >=0 with %d rows", C );
+  all(S(:)>=0) || ...
+      error( "S/D must contain nonnegative values" );
+  K = columns(S);
+  if ( nargin < 3 )
+    V = ones(size(S));
+  else
+    ( ismatrix(V) && size_equal(S,V) ) || ...
+	error( "V must be a %d x %d matrix", C, K);
+    all(V(:)>=0) || ...
+	error( "V must contain nonnegative values" );
+  endif
+
+  D = S.*V;
+  X_lower = zeros(1,C);
+  X_upper = 1./max(D,[],2)';
+  R_lower = sum(D,2)';
+  R_upper = +inf(1,C);
+endfunction
+
+%!test
+%! fail( "qnomaba( [1 1], [1 1 1; 1 1 1; 1 1 1] )", "2 rows" );
diff --git a/inst/qnomvisits.m b/inst/qnomvisits.m
new file mode 100644
index 0000000..8741dca
--- /dev/null
+++ b/inst/qnomvisits.m
@@ -0,0 +1,144 @@
+## Copyright (C) 2012, 2013 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} =} qnomvisits (@var{P}, @var{lambda})
+##
+## Compute the visit ratios to the service centers of an open multiclass network with @math{K} service centers and @math{C} customer classes.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @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
+## @code{@var{lambda}(r,i)} is the external arrival rate of class @math{r}
+## requests to center @math{i}.
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item V
+## @code{@var{V}(r,i)} is the visit ratio of class @math{r}
+## requests at center @math{i}.
+##
+## @end table
+## 
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function V = qnomvisits( P, lambda )
+
+  if ( nargin != 2 )
+    print_usage();
+  endif
+
+  ndims(P) == 4 || ...
+      error("P must be a 4-dimensional matrix");
+
+  [C, K, C2, K2] = size( P );
+  (K == K2 && C == C2) || ...
+      error( "P must be a [%d,%d,%d,%d] matrix", C, K, C, K);
+
+  ( ndims(lambda) == 2 && [C,K] == size(lambda) ) || ...
+      error( "lambda must be a %d x %d matrix", C, K );
+
+  all(lambda(:)>=0) || ...
+      error(" lambda contains negative values" );
+
+  ## 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)
+  A = eye(K*C) - reshape(P,[K*C K*C]);
+  b = reshape(lambda / sum(lambda(:)), [1,K*C]);
+  V = reshape(b/A, [C, K]);
+
+  ## Make sure that no negative values appear (sometimes, numerical
+  ## errors produce tiny negative values instead of zeros)
+  V = max(0,V);
+endfunction
+%!test
+%! fail( "qnomvisits( zeros(3,3,3), [1 1 1] )", "matrix");
+
+%!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(lambda(:));
+%! V = qnomvisits(P, lambda);
+%! assert( 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
+%! # example 7.7 p. 304 Bolch et al. 
+%! # Note that the book uses a slightly different notation than
+%! # what we use here. Specifically, the book defines the routing 
+%! # probabilities as P(i,r,j,s) (i,j are service centers, r,s are job
+%! # classes) while the queueing package uses P(r,i,s,j).
+%! # A more serious problem arises in the definition of external arrivals.
+%! # The computation of V(r,i) as given in the book (called e_ir
+%! # in Eq 7.14) is performed in terms of P_{0, js}, defined as 
+%! # "the probability in an open network that a job from outside the network
+%! #  enters the jth node as a job of the sth class" (p. 267). This is
+%! # compliant with eq. 7.12 where the external class r arrival rate at center
+%! # i is computed as \lambda * P_{0,ir}. However, example 7.7 wrongly
+%! # defines P_{0,11} = P_{0,12} = 1, instead of P_{0,11} = P_{0,12} = 0.5
+%! # Therefore the resulting visit ratios they obtain must be divided by two.
+%! P = zeros(2,3,2,3);
+%! lambda = S = zeros(2,3);
+%! P(1,1,1,2) = 0.4;
+%! P(1,1,1,3) = 0.3;
+%! P(1,2,1,1) = 0.6;
+%! P(1,2,1,3) = 0.4;
+%! P(1,3,1,1) = 0.5;
+%! P(1,3,1,2) = 0.5;
+%! P(2,1,2,2) = 0.3;
+%! P(2,1,2,3) = 0.6;
+%! P(2,2,2,1) = 0.7;
+%! P(2,2,2,3) = 0.3;
+%! P(2,3,2,1) = 0.4;
+%! P(2,3,2,2) = 0.6;
+%! lambda(1,1) = lambda(2,1) = 1;
+%! V = qnomvisits(P,lambda);
+%! assert( V, [ 3.333 2.292 1.917; 10 8.049 8.415] ./ 2, 1e-3);
diff --git a/inst/qnopen.m b/inst/qnopen.m
new file mode 100644
index 0000000..2314d74
--- /dev/null
+++ b/inst/qnopen.m
@@ -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{qnos, qnom}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)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] = qnos(lambda, S, V, varargin{:});
+  else
+    [U R Q X] = qnom(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, diag( lambda / sum(lambda) ) * 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 );
+
diff --git a/inst/qnopenab.m b/inst/qnopenab.m
new file mode 100644
index 0000000..e328461
--- /dev/null
+++ b/inst/qnopenab.m
@@ -0,0 +1,39 @@
+## Copyright (C) 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}] =} qnopenab (@var{lambda}, @dots{} )
+##
+## This function is deprecated. Please use @code{qnosaba} instead.
+##
+## @seealso{qnosaba}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xu Rl] = qnopenab( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnopenab is deprecated. Please use qnosaba instead");
+  endif
+  [Xl Xu Rl Ru] = qnosaba( varargin{:} );
+endfunction
diff --git a/inst/qnopenbsb.m b/inst/qnopenbsb.m
new file mode 100644
index 0000000..c2444ee
--- /dev/null
+++ b/inst/qnopenbsb.m
@@ -0,0 +1,50 @@
+## Copyright (C) 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}, @dots{})
+##
+## @cindex bounds, balanced system
+## @cindex open network
+##
+## This function is deprecated. Please use @code{qnosbsb} instead.
+##
+## @seealso{qnosbsb}
+## 
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [Xu Rl Ru] = qnopenbsb( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnopenbsb is deprecated. Please use qnosbsb instead");
+  endif
+  [Xl Xu Rl Ru] = qnosbsb( varargin{:} );
+endfunction
+
+%!test
+%! fail( "qnopenbsb( 0.1, [] )", "vector" );
+%! fail( "qnopenbsb( 0.1, [0 -1])", "nonnegative" );
+%! fail( "qnopenbsb( 0, [1 2] )", "lambda" );
+%! fail( "qnopenbsb( -1, [1 2])", "lambda" );
+
+
diff --git a/inst/qnopenmulti.m b/inst/qnopenmulti.m
new file mode 100644
index 0000000..ef5cd9b
--- /dev/null
+++ b/inst/qnopenmulti.m
@@ -0,0 +1,39 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qnom} instead.
+##
+## @seealso{qnom}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+function [U R Q X] = qnopenmulti( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnopenmulti is deprecated. Please use qnom instead");
+  endif
+  [U R Q X] = qnom( varargin{:} );
+endfunction
diff --git a/inst/qnopensingle.m b/inst/qnopensingle.m
new file mode 100644
index 0000000..23a9ed2
--- /dev/null
+++ b/inst/qnopensingle.m
@@ -0,0 +1,40 @@
+## Copyright (C) 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})
+##
+## This function is deprecated. Please use @code{qnos} instead.
+##
+## @seealso{qnos}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnopensingle( varargin )
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnopensingle is deprecated. Please use qnos instead");
+  endif
+  [U R Q X] = qnos( varargin{:} );
+endfunction
diff --git a/inst/qnos.m b/inst/qnos.m
new file mode 100644
index 0000000..a93122d
--- /dev/null
+++ b/inst/qnos.m
@@ -0,0 +1,216 @@
+## 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}] =} qnos (@var{lambda}, @var{S}, @var{V}) 
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}] =} qnos (@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{k} can consist of @code{@var{m}(k) @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}, enter @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 all @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,qnosvisits}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X] = qnos( varargin )
+  if ( nargin < 3 || nargin > 4 )
+    print_usage();
+  endif
+
+  [err lambda S V m] = qnoschkparam( varargin {:} );
+  isempty(err) || error(err);
+
+  all(S>0) || ...
+      error( "S must be positive" );
+
+  ## If there are M/M/k servers with k>=1, compute the maximum
+  ## processing capacity
+  m(m<1) = -1; # avoids division by zero in next line
+  [Umax kmax] = max( lambda * S .* V ./ m );
+  (Umax < 1) || ...
+      error( "Processing capacity exceeded at center %d", kmax );
+
+  l = lambda*V; # arrival rates
+
+  i = find( m == 1 ); # single station queueing centers
+  if numel(i)
+    [U(i) R(i) Q(i) X(i)] = qsmm1( l(i), 1./S(i) );
+  endif
+  
+  i = find( m<1 ); # delay centers
+  if numel(i)
+    [U(i) R(i) Q(i) X(i)] = qsmminf( 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)] = qsmmm( l(i), 1./S(i), m(i) );
+  endif
+endfunction
+%!test
+%! lambda = 0;
+%! S = [1 1 1];
+%! V = [1 1 1];
+%! fail( "qnos(lambda,S,V)","lambda must be");
+%! lambda = 1;
+%! S = [1 0 1];
+%! fail( "qnos(lambda,S,V)","S must be");
+%! S = [1 1 1];
+%! m = [1 1];
+%! fail( "qnos(lambda,S,V,m)","incompatible size");
+%! V = [1 1 1 1];
+%! fail( "qnos(lambda,S,V)","incompatible size");
+%! fail( "qnos(1.0, [0.9 1.2], [1 1])", "exceeded at center 2");
+%! fail( "qnos(1.0, [0.9 2.0], [1 1], [1 2])", "exceeded at center 2");
+%! qnos(1.0, [0.9 1.9], [1 1], [1 2]); # should not fail
+%! qnos(1.0, [0.9 1.9], [1 1], [1 0]); # should not fail
+%! qnos(1.0, [1.9 1.9], [1 1], [0 0]); # should not fail
+%! qnos(1.0, [1.9 1.9], [1 1], [2 2]); # should not fail
+ 
+%!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] = qnos( lambda, S, V );
+%! assert( R, [0.0192 0.0345 0.107], 1e-2 );
+%! assert( U, [0.48 0.42 0.72], 1e-2 );
+%! assert( Q, R.*X, 1e-5 ); # check Little's Law
+
+%!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] = qnos( lambda, S, V );
+%! assert( U(1), 0.182, 1e-3 );
+%! assert( X(1), 36.3, 1e-2 );
+%! assert( Q(1), 0.222, 1e-3 );
+%! assert( Q, R.*X, 1e-5 ); # check Little's Law
+
+%!test
+%! lambda=[1];
+%! P=[0];
+%! V=qnosvisits(P,lambda);
+%! S=[0.25];
+%! [U1 R1 Q1 X1]=qnos(sum(lambda),S,V); 
+%! [U2 R2 Q2 X2]=qsmm1(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 );
+
+## Check if processing capacity is properly accounted for
+%!test
+%! lambda = 1.1;
+%! V = 1;
+%! m = [2];
+%! S = [1];
+%! [U1 R1 Q1 X1] = qnos(lambda,S,V,m); 
+%! m = [-1];
+%! lambda = 90.0;
+%! [U1 R1 Q1 X1] = qnos(lambda,S,V,m); 
+
+%!demo
+%! lambda = 3;
+%! V = [16 7 8];
+%! S = [0.01 0.02 0.03];
+%! [U R Q X] = qnos( lambda, S, V );
+%! R_s = dot(R,V) # System response time
+%! N = sum(Q) # Average number in system
+
+%!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];
+%! V=qnosvisits(P,lambda);
+%! k = [ 3 2 4 1 ];
+%! [U R Q X] = qnos( sum(lambda), S, V );
+%! 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 );
diff --git a/inst/qnosaba.m b/inst/qnosaba.m
new file mode 100644
index 0000000..c0a85a5
--- /dev/null
+++ b/inst/qnosaba.m
@@ -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}, @var{Rl}, @var{Ru}] =} qnosaba (@var{lambda}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnosaba (@var{lambda}, @var{S}, @var{V})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnosaba (@var{lambda}, @var{S}, @var{V}, @var{m})
+##
+## @cindex bounds, asymptotic
+## @cindex open network
+##
+## Compute Asymptotic Bounds for open, single-class networks.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate of requests (@code{@var{lambda} @geq{} 0}).
+##
+## @item D
+## @code{@var{D}(k)} is the service demand at center @math{k}.
+## (@code{@var{D}(k) @geq{} 0} for all @math{k}).
+##
+## @item S
+## @code{@var{S}(k)} is the mean service time at center @math{k}.
+## (@code{@var{S}(k) @geq{} 0} for all @math{k}).
+##
+## @item V
+## @code{@var{V}(k)} is the mean number of visits to center @math{k}.
+## (@code{@var{V}(k) @geq{} 0} for all @math{k}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center @math{k}.
+## This function only supports @math{M/M/1} queues, therefore
+## @var{m} must be @code{ones(size(S))}. 
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @item Xu
+## Lower and upper bounds on the system throughput. @var{Xl} is
+## always set to @math{0} since there can be no lower bound on the
+## throughput of open networks.
+##
+## @item Rl
+## @item Ru
+## Lower and upper bounds on the system response time. @var{Ru}
+## is always set to @code{+inf} since there can be no upper bound on the
+## throughput of open networks.
+##
+## @end table
+##
+## @seealso{qnopenmultiab}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_lower X_upper R_lower R_upper] = qnosaba( varargin )
+  if ( nargin < 2 || nargin > 4 )
+    print_usage();
+  endif
+
+  [err lambda S V m] = qnoschkparam( varargin{:} );
+  isempty(err) || error(err);
+  
+  all(m==1) || ...
+      error("this function supports M/M/1 servers only");
+
+  D = S .* V;
+
+  X_lower = 0;
+  X_upper = 1/max(D);
+  R_lower = sum(D);
+  R_upper = +inf;
+endfunction
+
+%!test
+%! fail( "qnosaba( 0.1, [] )", "vector" );
+%! fail( "qnosaba( 0.1, [0 -1])", "nonnegative" );
+%! fail( "qnosaba( 0, [1 2] )", "lambda" );
+%! fail( "qnosaba( -1, [1 2])", "lambda" );
+%! fail( "qnosaba( 1, [1 2 3], [1 2] )", "incompatible size");
+%! fail( "qnosaba( 1, [1 2 3], [-1 2 3] )", "nonnegative");
+
+%!test
+%! [Xl Xu Rl Ru] = qnosaba( 1, [1 1] );
+%! assert( Xl, 0 );
+%! assert( Ru, +inf );
diff --git a/inst/qnosbsb.m b/inst/qnosbsb.m
new file mode 100644
index 0000000..118f900
--- /dev/null
+++ b/inst/qnosbsb.m
@@ -0,0 +1,109 @@
+## 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}] =} qnosbsb (@var{lambda}, @var{D})
+## @deftypefnx {Function File} {[@var{Xl}, @var{Xu}, @var{Rl}, @var{Ru}] =} qnosbsb (@var{lambda}, @var{S}, @var{V})
+##
+## @cindex bounds, balanced system
+## @cindex open network
+##
+## Compute Balanced System Bounds for single-class, open networks.
+##
+## @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}.
+## (@code{@var{D}(k) @geq{} 0} for all @math{k}).
+##
+## @item S
+## @code{@var{S}(k)} is the mean service time at center @math{k}.
+## (@code{@var{S}(k) @geq{} 0} for all @math{k}).
+##
+## @item V
+## @code{@var{V}(k)} is the mean number of visits at center @math{k}.
+## (@code{@var{V}(k) @geq{} 0} for all @math{k}).
+##
+## @item m
+## @code{@var{m}(k)} is the number of servers at center @math{k}.
+## This function only supports @math{M/M/1} queues, therefore
+## @var{m} must be @code{ones(size(S))}. 
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item Xl
+## @item Xu
+## Lower and upper bounds on the system throughput. @var{Xl} is always
+## set to @math{0}, since there can be no lower bound on open
+## networks throughput.
+##
+## @item Rl
+## @itemx Ru
+## Lower and upper bounds on the system response time.
+##
+## @end table
+##
+## @seealso{qnosaba}
+## 
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [X_lower X_upper R_lower R_upper] = qnosbsb( varargin )
+  if ( nargin < 2 || nargin > 4 )
+    print_usage();
+  endif
+
+  [err lambda S V m] = qnoschkparam( varargin{:} );
+  isempty(err) || error(err);
+
+  all(m==1) || ...
+      error("this function supports M/M/1 servers only");
+  
+  D = S .* V;
+
+  D_max = max(D);
+  D_tot = sum(D);
+  D_ave = mean(D_tot);
+  X_upper = 1/D_max;
+  X_lower = 0;
+  R_lower = D_tot / (1-lambda*D_ave);
+  R_upper = D_tot / (1-lambda*D_max);
+endfunction
+
+%!test
+%! fail( "qnosbsb( 0.1, [] )", "vector" );
+%! fail( "qnosbsb( 0.1, [0 -1])", "nonnegative" );
+%! fail( "qnosbsb( 0, [1 2] )", "lambda" );
+%! fail( "qnosbsb( -1, [1 2])", "lambda" );
+
+%!test
+%! [Xl Xu Rl Ru] = qnosbsb(0.1,[1 2 3]);
+%! assert( Xl, 0 );
+
diff --git a/inst/qnosvisits.m b/inst/qnosvisits.m
new file mode 100644
index 0000000..84e73f7
--- /dev/null
+++ b/inst/qnosvisits.m
@@ -0,0 +1,115 @@
+## Copyright (C) 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} =} qnosvisits (@var{P}, @var{lambda})
+##
+## Compute the average number of visits to the service centers of a single 
+## class open Queueing Network with @math{K} service centers.
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item P
+## @code{@var{P}(i,j)} is the probability that a request which completed
+## service at center @math{i} is routed to center @math{j}. 
+##
+## @item lambda
+## @code{@var{lambda}(i)} is the external arrival rate to
+## center @math{i}. 
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item V
+## @code{@var{V}(i)} is the average number of
+## visits to server @math{i}.
+##
+## @end table
+## 
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function V = qnosvisits( P, lambda )
+
+  if ( nargin != 2 )
+    print_usage();
+  endif
+
+  issquare(P) || ...
+      error("P must be a square matrix");
+
+  K = rows(P);
+
+  all(P(:)>=0) && all( sum(P,2)<=1+1e-5 ) || ...
+      error( "invalid transition probability matrix P" );
+  
+  ( isvector(lambda) && length(lambda) == K ) || ...
+      error( "lambda must be a vector with %d elements", K );
+
+  all( lambda>= 0 ) || ...
+      error( "lambda contains negative values" );
+
+  lambda = lambda(:)';
+
+  V = zeros(size(P));  
+  A = eye(K)-P;
+  b = lambda / sum(lambda);
+  V = b/A;
+  ## Make sure that no negative values appear (sometimes, numerical
+  ## errors produce tiny negative values instead of zeros)
+  V = max(0,V);
+endfunction
+%!test
+%! fail( "qnosvisits([0 .5; .5 0],[0 -1])", "contains negative" );
+%! fail( "qnosvisits([1 1 1; 1 1 1], [1 1])", "square" );
+
+%!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 = qnosvisits(P,lambda);
+%! assert( V*P+lambda/sum(lambda),V,1e-5 );
+
+%!demo
+%! p = 0.3;
+%! lambda = 1.2
+%! P = [0 0.3 0.5; ...
+%!      1 0   0  ; ...
+%!      1 0   0  ];
+%! V = qnosvisits(P,[1.2 0 0])
+
+%!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 = qnosvisits(P,lambda);
+%! S = [2 1 2 1.8];
+%! m = [3 1 1 2];
+%! [U R Q X] = qnos( sum(lambda), S, V, m )
+
diff --git a/inst/qnsolve.m b/inst/qnsolve.m
new file mode 100644
index 0000000..ccb561b
--- /dev/null
+++ b/inst/qnsolve.m
@@ -0,0 +1,820 @@
+## 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})
+##
+## High-level function for analyzing QN models.
+##
+## @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 <moreno.marzolla(at)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) || ...
+      error("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
+    error( "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) ) || ...
+      error( "lambda must be a scalar > 0" );
+  
+  iscell(QQ) || ...
+      error( "QQ must be a cell array" );
+
+  N = length(QQ);
+
+  ( isvector(V) && length(V) == N ) || ...
+      error( "V must be a vector of length %d", N );
+
+  V = V(:); # make V a row vector
+  all(V>=0) || ...
+      error( "V must be >= 0" );
+
+  ## Initialize vectors
+  S = zeros(size(V));
+  m = ones(size(V));
+  for i=1:N
+    QQ{i}.c == 1 || ...
+	error( "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
+      error( "Unsupported type \"%s\" for node %d", QQ{i}.node, i );
+    endif
+  endfor
+
+  [U R Q X] = qnos( 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) || ...
+      error( "lambda must be a vector >0" );
+  lambda = lambda(:)'; # make lambda a row vector
+  iscell(QQ) || ...
+      error( "QQ must be a cell array" );
+  C = length(lambda);
+  K = length(QQ);
+  [C,K] == size(V) || ...
+      error( "V size mismatch" );
+  all( all( V>= 0 ) ) || ...
+      error( "V must be >= 0 " );
+
+  S = zeros(C,K);
+  m = ones(1,K);
+  for i=1:K
+    QQ{i}.c == C || ...
+	error( "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
+      error( "Unsupported type \"%s\" for node %d", QQ{i}.node, i );
+    endif  
+  endfor
+
+  [U R Q X] = qnom( 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 )
+    error();
+  endif
+
+  isscalar(N) || ...
+      error( "Multiclass networks are not supported by this function" );
+
+  iscell(QQ) || ...
+      error( "QQ must be a cell array" );
+
+  if ( nargin < 4 ) 
+    Z = 0;
+  else
+    isscalar(Z) && Z >= 0 || ...
+        error( "Z must be >= 0" );
+  endif
+
+  K = length(QQ);
+  
+  ( isvector(V) && length(V) == K ) || ...
+      error( "V must be a vector of length %d", K );
+
+  found_ld = false;
+  for k=1:K
+    if ( __is_ld(QQ{k}) )
+      found_ld = true;
+      break;
+    endif
+  endfor
+
+  if ( found_ld )
+    S = zeros(K, N);
+    for k=1:K
+      ( QQ{k}.c == 1 ) || ...
+	  error( "Multiclass networks are not supported by this function" );
+      if __is_li(QQ{k})
+	S(k,:) = QQ{k}.S;
+      elseif __is_multi(QQ{k})
+	S(k,:) = QQ{k}.S ./ min(1:N,QQ{k}.m);
+      elseif __is_is(QQ{k})
+	S(k,:) = QQ{k}.S ./ (1:N);
+      elseif __is_ld(QQ{k})
+	S(k,:) = QQ{k}.S;
+      else
+	error( "Unsupported type \"%s\" for node %d", QQ{k}.node, k );
+      endif      
+    endfor
+    [U R Q X] = qncsmvald(N, S, V, Z);
+  else
+    S = zeros(1,K);
+    m = ones(1,K);
+    for k=1:K
+      ( QQ{k}.c == 1 ) || ...
+	  error( "Multiclass networks are not supported by this function" );
+      S(k) = QQ{k}.S;
+      if __is_li(QQ{k})
+	# nothing to do
+      elseif __is_multi(QQ{k})
+	m(k) = QQ{k}.m;
+      elseif __is_is(QQ{k})
+	m(k) = -1;
+      else
+	error( "Unsupported type \"%s\" for node %d", QQ{k}.node, k );
+      endif
+    endfor
+    [U R Q X] = qncsmva(N, S, V, m, Z);
+  endif
+
+#{
+
+  ## TODO: remove this
+
+  ## Initialize vectors
+  i_single = i_multi = i_delay = i_ld = [];
+  for i=1:K
+    ( QQ{i}.c == 1 ) || ...
+	error( "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
+      error( "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 ) || ...
+      error( "N must be >0" );
+
+  iscell(QQ) || ...
+      error( "QQ must be a cell array" );
+
+  C = length(N); ## Number of classes
+  K = length(QQ); ## Number of service centers
+  size(V) == [C,K] || ...
+      error( "V size mismatch" );
+
+  if ( nargin < 4 )
+    Z = zeros(1,C);
+  else
+    isvector(Z) && length(Z) == C || ...
+	error( "Z size mismatch" );
+  endif
+
+  ## Check consistence of parameters
+  all( all( V >= 0 ) ) || ...
+      error( "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 ) || ...
+	error( "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 )) || ...
+	  error( "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) || ...
+	  error( "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
+      error( "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 = qncmpopmix( 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) ) || ...
+      error( "lambda and N must be vectors of the same size" );
+  ( iscell(QQ) && length(QQ) == length(lambda) ) || ...
+      error( "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})
+      error( "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)
+    error( "N and lambda are of incompatible size" );
+  endif
+  ( isvector(N) && isvector(lambda) && size_equal(lambda,N) ) || ...
+      error( "N and lambda must be vector of the same length" );
+  C = length(N);  
+  K = length(QQ); # number of service centers
+
+  [C,K] == size(V) || ...
+      error( "V size mismatch" );
+  [C,K] == size(U) || ...
+      error( "U size mismatch" );
+  [C,K] == size(R) || ...
+      error( "R size mismatch" );
+  [C,K] == size(Q) || ...
+      error( "Q size mismatch" );
+  [C,K] == size(X) || ...
+      error( "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, qnosvisits(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 }, diag( lambda / sum(lambda) ) * 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 );
diff --git a/inst/qnvisits.m b/inst/qnvisits.m
new file mode 100644
index 0000000..f06625d
--- /dev/null
+++ b/inst/qnvisits.m
@@ -0,0 +1,515 @@
+## 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.
+##
+## This function is deprecated. Please use one of @code{qncsvisits},
+## @code{qncmvisits}, @code{qnosvisits} or @code{qnomvisits} instead.
+##
+## @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 function is called with a single
+## argument, 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}, assuming center 1 as the reference station
+## (i.e., @code{@var{V}(1) = 1}). For multiple class networks,
+## @code{@var{V}(r,i)} is the number of visits of class @math{r}
+## requests at center @math{i}.
+##
+## @item ch
+## (For closed networks only). @code{@var{ch}(c)} is the chain number
+## that class @math{c} belongs to. Different classes can belong to the
+## same chain. Chains are numbered sequentially starting from 1
+## (@math{1, 2, @dots{}}). The total number of chains is
+## @code{max(@var{ch})}.
+##
+## @end table
+## 
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [V ch] = qnvisits( P, varargin )
+
+  persistent warned = false;
+  if (!warned)
+    warned = true;
+    warning("qn:deprecated-function",
+	    "qnvisits is deprecated. Please use one of qncsvisits, qnosvisits, qncmvisits, qnomvisits instead");
+  endif
+
+  if ( nargin < 1 || nargin > 2 )
+    print_usage();
+  endif
+
+  ( ndims(P) == 2 || ndims(P) == 4 ) || ...
+      error("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 transition probability" );
+%! 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 transition probability matrices and tries
+%! ## to compute the visit counts on them. 
+%!
+%! for k=[5, 10, 20, 50]
+%!   P = reshape(1:k^2, k, k);
+%!   P = P ./ repmat(sum(P,2),1,k);
+%!   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(lambda(:));
+%! V = qnvisits(P, lambda);
+%! assert( 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 );
+
+%!test
+%! C = 2;
+%! K = 3;
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = 1;
+%! P(1,2,1,3) = 1;
+%! P(1,3,2,2) = 1;
+%! P(2,2,1,1) = 1;
+%! [V ch] = qnvisits(P);
+%! assert( ch, [1 1] );
+
+## The following transition probability matrix is not well formed: note
+## that there is an outgoing transition from center 1, class 1 but not
+## incoming transition.
+%!test
+%! C = 2;
+%! K = 3;
+%! P = zeros(C,K,C,K);
+%! P(1,1,1,2) = 1;
+%! P(1,2,1,3) = 1;
+%! P(1,3,2,2) = 1;
+%! P(2,2,2,1) = 1;
+%! P(2,1,1,2) = 1;
+%! [V ch] = qnvisits(P);
+%! assert( ch, [1 1] );
+
+##############################################################################
+## 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) || ...
+      error( "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,j) = 1 for all s and for a
+    ## chosen server j visited by chain s requests (see below)
+    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 center 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 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) || ...
+        error( "lambda size mismatch" );
+    
+    ## solve the traffic equation
+    A = eye(K*C) - reshape(P,[K*C K*C]);
+    b = reshape(lambda / 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,
+## which requires two DFS visits. A better solution would be to use
+## Tarjan's algorithm.
+##
+## 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);
+      if (any(r))
+	s(r) = c++;
+      endif
+    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 )
+
+  persistent epsilon = 10*eps;
+
+  issquare(P)  || ...
+      error( "P must be a square matrix" );
+
+  N = size(P,1);
+  V = zeros(N,N);
+
+  if ( nargin < 2 )
+    ##
+    ## Closed network
+    ##
+    [res err] = dtmcchkP(P);
+    (res>0) || ...
+        error( "P is not a transition probability 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
+    ##
+    all(P(:)>=0) && all( sum(P,2)<=1+1e-5 ) || ...
+	error( "P is not a transition probability matrix for open networks" );
+
+    ( isvector(lambda) && length(lambda) == N ) || ...
+        error( "lambda size mismatch" );
+    all( lambda>= 0 ) || ...
+        error( "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
+
diff --git a/inst/qsammm.m b/inst/qsammm.m
new file mode 100644
index 0000000..4ceb318
--- /dev/null
+++ b/inst/qsammm.m
@@ -0,0 +1,109 @@
+## 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}] =} qsammm (@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{qsmmm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pm] = qsammm( lambda, mu )
+  if ( nargin != 2 )
+    print_usage();
+  endif
+
+  ( isscalar(lambda) && isvector(mu) ) || ...
+      error( "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;
+#}
+  p0 = 1 / ( ...
+            sumexpn( m*rho, m-1 ) + ...
+            expn(m*rho, m) / (1-rho) ...
+            );
+  pm = expn(m*rho, m)*p0/(1-rho);
+  Q = m*rho+rho / (1-rho) * pm;
+  R = Q / X;
+endfunction
+%!test
+%! [U R Q X] = qsammm( 73,[10,15,20,20,25] );
+%! assert( U, 0.81, 1e-2 );
+%! assert( Q, 6.5278, 1e-4 );
+
+
diff --git a/inst/qsmg1.m b/inst/qsmg1.m
new file mode 100644
index 0000000..8633f35
--- /dev/null
+++ b/inst/qsmg1.m
@@ -0,0 +1,101 @@
+## Copyright (C) 2009 Dmitry Kolesnikov
+## Copyright (C) 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}] =} qsmg1 (@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{qsmh1}
+##
+## @end deftypefn
+
+## Author: Dmitry Kolesnikov
+
+function [U R Q X p0] = qsmg1(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 ) 
+      error( "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
diff --git a/inst/qsmh1.m b/inst/qsmh1.m
new file mode 100644
index 0000000..89eb5f7
--- /dev/null
+++ b/inst/qsmh1.m
@@ -0,0 +1,114 @@
+## Copyright (C) 2009 Dmitry Kolesnikov
+## Copyright (C) 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}] =} qsmh1 (@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:
+##
+## @tex
+## $$ B(x) = \sum_{j=1}^m \alpha_j(1-e^{-\mu_j x}),\quad x>0 $$
+## @end tex
+##
+## @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
+##
+## @end deftypefn
+
+## Author: Dmitry Kolesnikov
+
+function [U R Q X p0] = qsmh1(lambda, mu, alpha)
+   if ( nargin != 3 )
+      print_usage();
+   endif
+   if ( size(mu) != size(alpha) )
+      error( "parameters are of incompatible size" );
+   endif
+   [n c] = size(mu);
+
+   if (!is_scalar(lambda) && (n != length(lambda)) ) 
+      error( "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] = qsmg1(lambda, xavg, x2nd);
+endfunction
diff --git a/inst/qsmm1.m b/inst/qsmm1.m
new file mode 100644
index 0000000..055e717
--- /dev/null
+++ b/inst/qsmm1.m
@@ -0,0 +1,114 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 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}] =} qsmm1 (@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.
+##
+## @tex
+## The steady-state probability @math{\pi_k} that there are @math{k}
+## jobs in the system, @math{k \geq 0}, can be computed as:
+##
+## $$
+## \pi_k = (1-\rho)\rho^k
+## $$
+##
+## where @math{\rho = \lambda/\mu} is the server utilization.
+##
+## @end tex
+##
+## @strong{INPUTS}
+##
+## @table @var
+##
+## @item lambda
+## Arrival rate (@code{@var{lambda} @geq{} 0}).
+##
+## @item mu
+## Service rate (@code{@var{mu} > @var{lambda}}).
+##
+## @end table
+##
+## @strong{OUTPUTS}
+##
+## @table @var
+##
+## @item U
+## Server utilization
+##
+## @item R
+## Server response time
+##
+## @item Q
+## Average number of requests in the system
+##
+## @item X
+## Server throughput. If the system is ergodic (@code{@var{mu} >
+## @var{lambda}}), we 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{qsmmm, qsmminf, qsmmmk}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0] = qsmm1( lambda, mu )
+  if ( nargin != 2 )
+    print_usage();
+  endif
+  ( isvector(lambda) && isvector(mu) ) || ...
+      error( "lambda and mu must be vectors" );
+  [ err lambda mu ] = common_size( lambda, mu );
+  if ( err ) 
+    error( "parameters are of incompatible size" );
+  endif
+  lambda = lambda(:)';
+  mu = mu(:)';
+  all( lambda >= 0 ) || ...
+      error( "lambda must be >= 0" );
+  all( mu > lambda ) || ...
+      error( "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( "qsmm1(10,5)", "not ergodic" );
+%! fail( "qsmm1([2 2], [1 1 1])", "incompatible size");
+
+%!test
+%! [U R Q X P0] = qsmm1(0, 1);
+%! assert( U, 0 );
+%! assert( R, 1 );
+%! assert( Q, 0 );
+%! assert( X, 0 );
+%! assert( P0, 1 );
diff --git a/inst/qsmm1k.m b/inst/qsmm1k.m
new file mode 100644
index 0000000..0b4ac4e
--- /dev/null
+++ b/inst/qsmm1k.m
@@ -0,0 +1,148 @@
+## 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}] =} qsmm1k (@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}.
+##
+## @tex
+## 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:
+##
+## $$
+## \pi_k = {(1-a)a^k \over 1-a^{K+1}}
+## $$
+##
+## where @math{a = \lambda/\mu}.
+## @end tex
+##
+## @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- at 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{qsmm1,qsmminf,qsmmm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pK] = qsmm1k( lambda, mu, K )
+  if ( nargin != 3 )
+    print_usage();
+  endif
+
+  ( isvector(lambda) && isvector(mu) && isvector(K) ) || ...
+      error( "lambda, mu, K must be vectors of the same size" );
+
+  [err lambda mu K] = common_size( lambda, mu, K );
+  if ( err ) 
+    error( "Parameters are not of common size" );
+  endif
+
+  all( K>0 ) || ...
+      error( "K must be >0" );
+  ( all( lambda>0 ) && all( mu>0 ) ) || ...
+      error( "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] = qsmm1k(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] = qsmm1k( lambda, mu, K );
+%! birth = lambda*ones(1,K);
+%! death = mu*ones(1,K);
+%! q = ctmc(ctmc_bd( birth, death ));
+%! U2 = 1-q(1);
+%! Q2 = dot( [0:K], q );
+%! assert( U1, U2, 1e-4 );
+%! assert( Q1, Q2, 1e-4 );
+
diff --git a/inst/qsmminf.m b/inst/qsmminf.m
new file mode 100644
index 0000000..189106f
--- /dev/null
+++ b/inst/qsmminf.m
@@ -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}] =} qsmminf (@var{lambda}, @var{mu})
+##
+## Compute utilization, response time, average number of requests and throughput for a @math{M/M/\infty} queue.
+##
+## The @math{M/M/\infty} system has an infinite number of identical
+## servers; this kind of system is always stable for every arrival and
+## service rates.
+##
+## @cindex @math{M/M/}inf system
+##
+## @tex
+## The steady-state probability @math{\pi_k} that there are @math{k}
+## requests in the system, @math{k @geq{} 0}, can be computed as:
+##
+## $$
+## \pi_k = {1 \over k!} \left( \lambda \over \mu \right)^k e^{-\lambda / \mu}
+## $$
+## @end tex
+##
+## @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{qsmm1,qsmmm,qsmmmk}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0] = qsmminf( lambda, mu )
+  if ( nargin != 2 )
+    print_usage();
+  endif
+  ( isvector(lambda) && isvector(mu) ) || ...
+      error( "lambda and mu must be vectors" );
+  [ err lambda mu ] = common_size( lambda, mu );
+  if ( err ) 
+    error( "Parameters are of incompatible size" );
+  endif  
+  lambda = lambda(:)';
+  mu = mu(:)';
+  ( all( lambda>0 ) && all( mu>0 ) ) || ...
+      error( "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( "qsmminf( [1 2], [1 2 3] )", "incompatible size");
+%! fail( "qsmminf( [-1 -1], [1 1] )", ">0" );
diff --git a/inst/qsmmm.m b/inst/qsmmm.m
new file mode 100644
index 0000000..6076899
--- /dev/null
+++ b/inst/qsmmm.m
@@ -0,0 +1,153 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 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}] =} qsmmm (@var{lambda}, @var{mu})
+## @deftypefnx {Function File} {[@var{U}, @var{R}, @var{Q}, @var{X}, @var{p0}, @var{pm}] =} qsmmm (@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 servers connected to a single FCFS
+## queue.
+##
+## @tex
+## The steady-state probability @math{\pi_k} that there are @math{k}
+## jobs in the system, @math{k \geq 0}, can be computed as:
+##
+## $$
+## \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
+## }
+## $$
+##
+## 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:
+##
+## $$
+## \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
+##
+## @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{erlangc,qsmm1,qsmminf,qsmmmk}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pm] = qsmmm( lambda, mu, m )
+  if ( nargin < 2 || nargin > 3 )
+    print_usage();
+  endif
+  if ( nargin == 2 )
+    m = 1;
+  else
+    ( isnumeric(lambda) && isnumeric(mu) && isnumeric(m) ) || ...
+	error( "the parameters must be numeric vectors" );
+  endif
+  [err lambda mu m] = common_size( lambda, mu, m );
+  if ( err ) 
+    error( "parameters are not of common size" );
+  endif
+  lambda = lambda(:)';
+  mu = mu(:)';
+  m = m(:)';
+  all( m>0 ) || ...
+      error( "m must be >0" );
+  all( lambda>0 ) || ...
+      error( "lambda must be >0" );
+  X = lambda;
+  U = rho = lambda ./ (m .* mu );
+  all( U < 1 ) || ...
+      error( "Processing capacity exceeded" );
+  Q = p0 = pm = 0*lambda;
+  for i=1:length(lambda)
+    p0(i) = 1 / ( ...
+                 sumexpn( m(i)*rho(i), m(i)-1 ) + ...		 
+		 expn(m(i)*rho(i), m(i))/(1-rho(i)) ...
+                 );
+  endfor
+  pm = erlangc(lambda ./ mu, m);
+  Q = m .* rho .+ rho ./ (1-rho) .* pm;
+  R = Q ./ X;
+endfunction
+%!demo
+%! # 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] = qsmmm(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");
+
+
diff --git a/inst/qsmmmk.m b/inst/qsmmmk.m
new file mode 100644
index 0000000..627b2cd
--- /dev/null
+++ b/inst/qsmmmk.m
@@ -0,0 +1,222 @@
+## Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 Moreno Marzolla
+##
+## This file is part of the queueing toolbox.
+##
+## The queueing toolbox is free software: you can redistribute it and/or
+## Copyright (C) 2008, 2009, 2010, 2011, 2012 Moreno Marzolla
+##
+## 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}] =} qsmmmk (@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.
+##
+## @tex
+##
+## 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:
+##
+## $$
+## \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}
+## $$
+##
+## 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:
+##
+## $$
+## \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
+##
+## @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{qsmm1,qsmminf,qsmmm}
+##
+## @end deftypefn
+
+## Author: Moreno Marzolla <moreno.marzolla(at)unibo.it>
+## Web: http://www.moreno.marzolla.name/
+
+function [U R Q X p0 pK] = qsmmmk( lambda, mu, m, K )
+  if ( nargin != 4 )
+    print_usage();
+  endif
+
+  ( isvector(lambda) && isvector(mu) && isvector(m) && isvector(K) ) || ...
+      error( "lambda, mu, m, K must be vectors" );
+  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 ) 
+    error( "Parameters are not of common size" );
+  endif
+
+  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" );
+  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(ctmcbd(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] = qsmmmk(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] = qsmm1k(lambda,mu,k);
+%! [U2 R2 Q2 X2] = qsmmmk(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] = qsmmmk(lambda,mu,1,k);
+%! [U2 R2 Q2 X2 p02] = qsmm1k(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] = qsmmmk( lambda, mu, m, k );
+%! birth = lambda*ones(1,k);
+%! death = [ mu*linspace(1,m,m) mu*m*ones(1,k-m) ];
+%! q = ctmc(ctmcbd( 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] = qsmmmk( 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] = qsmmmk( 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] = qsmmmk( 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
+

-- 
Alioth's /home/groups/pkg-octave/bin/git-commit-notice on /srv/git.debian.org/git/pkg-octave/octave-queueing.git.git



More information about the Pkg-octave-commit mailing list