diff --git a/COPYING b/COPYING
new file mode 100644
--- /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/Protocol/Arithmetic.hs b/Protocol/Arithmetic.hs
new file mode 100644
--- /dev/null
+++ b/Protocol/Arithmetic.hs
@@ -0,0 +1,304 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Protocol.Arithmetic where
+
+import Control.Arrow (first)
+import Control.Monad (Monad(..))
+import Data.Bits
+import Data.Bool
+import Data.Eq (Eq(..))
+import Data.Foldable (Foldable, foldl', foldMap)
+import Data.Function (($), (.))
+import Data.Int (Int)
+import Data.Maybe (Maybe(..))
+import Data.Ord (Ord(..))
+import Data.Semigroup (Semigroup(..))
+import Data.String (IsString(..))
+import Numeric.Natural (Natural)
+import Prelude (Integer, Integral(..), fromIntegral, Enum(..))
+import Text.Show (Show(..))
+import qualified Control.Monad.Trans.State.Strict as S
+import qualified Crypto.Hash as Crypto
+import qualified Data.ByteArray as ByteArray
+import qualified Data.ByteString as BS
+import qualified Data.List as List
+import qualified Prelude as N
+import qualified System.Random as Random
+
+-- * Type 'F'
+-- | The type of the elements of a 'PrimeField'.
+--
+-- A field must satisfy the following properties:
+--
+-- * @(f, ('+'), 'zero')@ forms an abelian group,
+--   called the 'Additive' group of 'f'.
+--
+-- * @('NonNull' f, ('*'), 'one')@ forms an abelian group,
+--   called the 'Multiplicative' group of 'f'.
+--
+-- * ('*') is associative:
+--   @(a'*'b)'*'c == a'*'(b'*'c)@ and
+--   @a'*'(b'*'c) == (a'*'b)'*'c@.
+--
+-- * ('*') and ('+') are both commutative:
+--   @a'*'b == b'*'a@ and
+--   @a'+'b == b'+'a@
+--
+-- * ('*') and ('+') are both left and right distributive:
+--   @a'*'(b'+'c) == (a'*'b) '+' (a'*'c)@ and
+--   @(a'+'b)'*'c == (a'*'c) '+' (b'*'c)@
+--
+-- The 'Natural' is always within @[0..'fieldCharac'-1]@.
+newtype F p = F { unF :: Natural }
+ deriving (Eq,Ord,Show)
+
+inF :: forall p i. PrimeField p => Integral i => i -> F p
+inF i = F (abs (fromIntegral i `mod` fieldCharac @p))
+	where abs x | x < 0 = x + fieldCharac @p
+	            | otherwise = x
+
+instance PrimeField p => Additive (F p) where
+	zero = F 0
+	F x + F y = F ((x + y) `mod` fieldCharac @p)
+instance PrimeField p => Negable (F p) where
+	neg (F x) | x == 0 = zero
+	          | otherwise = F (fromIntegral (N.negate (toInteger x) + toInteger (fieldCharac @p)))
+instance PrimeField p => Multiplicative (F p) where
+	one = F 1
+	-- | Because 'fieldCharac' is prime,
+	-- all elements of the field are invertible modulo 'fieldCharac'.
+	F x * F y = F ((x * y) `mod` fieldCharac @p)
+instance PrimeField p => Random.Random (F p) where
+	randomR (F lo, F hi) =
+		first (F . fromIntegral) .
+		Random.randomR
+		 ( 0`max`toInteger lo
+		 , toInteger hi`min`(toInteger (fieldCharac @p) - 1))
+	random = first (F . fromIntegral) . Random.randomR (0, toInteger (fieldCharac @p) - 1)
+
+-- ** Class 'PrimeField'
+-- | Parameter for a prime field.
+class PrimeField p where
+	-- | The prime number characteristic of a 'PrimeField'.
+	--
+	-- ElGamal's hardness to decrypt requires a large prime number
+	-- to form the 'Multiplicative' 'SubGroup'.
+	fieldCharac :: Natural
+
+-- ** Class 'Additive'
+class Additive a where
+	zero :: a
+	(+) :: a -> a -> a; infixl 6 +
+	sum :: Foldable f => f a -> a
+	sum = foldl' (+) zero
+instance Additive Natural where
+	zero = 0
+	(+)  = (N.+)
+instance Additive Integer where
+	zero = 0
+	(+)  = (N.+)
+instance Additive Int where
+	zero = 0
+	(+)  = (N.+)
+
+-- *** Class 'Negable'
+class Additive a => Negable a where
+	neg :: a -> a
+	(-) :: a -> a -> a; infixl 6 -
+	x-y = x + neg y
+instance Negable Integer where
+	neg  = N.negate
+instance Negable Int where
+	neg  = N.negate
+
+-- ** Class 'Multiplicative'
+class Multiplicative a where
+	one :: a
+	(*) :: a -> a -> a; infixl 7 *
+instance Multiplicative Natural where
+	one = 1
+	(*) = (N.*)
+instance Multiplicative Integer where
+	one = 1
+	(*) = (N.*)
+instance Multiplicative Int where
+	one = 1
+	(*) = (N.*)
+
+-- ** Class 'Invertible'
+class Multiplicative a => Invertible a where
+	inv :: a -> a
+	(/) :: a -> a -> a; infixl 7 /
+	x/y = x * inv y
+
+-- * Type 'G'
+-- | The type of the elements of a 'Multiplicative' 'SubGroup' of a 'PrimeField'.
+newtype G q = G { unG :: F (P q) }
+ deriving (Eq,Ord,Show)
+
+-- | @('natG' g)@ returns the element of the 'SubGroup' 'g'
+-- as an 'Natural' within @[0..'fieldCharac'-1]@.
+natG :: SubGroup q => G q -> Natural
+natG = unF . unG
+
+instance (SubGroup q, Multiplicative (F (P q))) => Multiplicative (G q) where
+	one = G one
+	G x * G y = G (x * y)
+instance (SubGroup q, Multiplicative (F (P q))) => Invertible (G q) where
+	-- | NOTE: add 'groupOrder' so the exponent given to (^) is positive.
+	inv = (^ E (neg one + groupOrder @q))
+
+-- ** Class 'SubGroup'
+-- | A 'SubGroup' of a 'PrimeField'.
+-- Used for signing (Schnorr) and encrypting (ElGamal).
+class
+ ( PrimeField (P q)
+ , Multiplicative (F (P q))
+ ) => SubGroup q where
+	-- | Setting 'q' determines 'p', equals to @'P' q@.
+	type P q :: *
+	-- | A generator of the 'SubGroup'.
+	-- NOTE: since @F p@ is a 'PrimeField',
+	-- the 'Multiplicative' 'SubGroup' is cyclic,
+	-- and there are phi('fieldCharac'-1) many choices for the generator of the group,
+	-- where phi is the Euler totient function.
+	groupGen :: G q
+	-- | The order of the 'SubGroup'.
+	--
+	-- WARNING: 'groupOrder' MUST be a prime number dividing @('fieldCharac'-1)@
+	-- to ensure that ensures that ElGamal is secure in terms
+	-- of the DDH assumption.
+	groupOrder :: F (P q)
+	
+	-- | 'groupGenInverses' returns the infinite list
+	-- of 'inv'erse powers of 'groupGen':
+	-- @['groupGen' '^' 'neg' i | i <- [0..]]@,
+	-- but by computing each value from the previous one.
+	--
+	-- NOTE: 'groupGenInverses' is in the 'SubGroup' class in order to keep
+	-- computed terms in memory accross calls to 'groupGenInverses'.
+	--
+	-- Used by 'validableEncryption'.
+	groupGenInverses :: [G q]
+	groupGenInverses = go one
+		where
+		go g = g : go (g * invGen)
+		invGen = inv groupGen
+
+-- | @('hash' prefix gs)@ returns as a number in @('F' p)@
+-- the SHA256 of the given 'prefix' prefixing the decimal representation
+-- of given 'SubGroup' elements 'gs', each one postfixed with a comma (",").
+--
+-- Used by 'proveEncryption' and 'validateEncryption',
+-- where the 'prefix' contains the 'statement' to be proven,
+-- and the 'gs' contains the 'commitments'.
+hash ::
+ SubGroup q =>
+ BS.ByteString -> [G q] -> E q
+hash prefix gs =
+	let s = prefix <> foldMap (\(G (F i)) -> fromString (show i) <> fromString ",") gs in
+	let h = ByteArray.convert (Crypto.hashWith Crypto.SHA256 s) in
+	inE (BS.foldl' (\acc b -> acc`shiftL`3 + fromIntegral b) (0::Natural) h)
+
+-- * Type 'E'
+-- | An exponent of a (necessarily cyclic) 'SubGroup' of a 'PrimeField'.
+-- The value is always in @[0..'groupOrder'-1]@.
+newtype E q = E { unE :: F (P q) }
+ deriving (Eq,Ord,Show)
+
+inE :: forall q i. SubGroup q => Integral i => i -> E q
+inE i = E (F (abs (fromIntegral i `mod` unF (groupOrder @q))))
+	where abs x | x < 0 = x + unF (groupOrder @q)
+	            | otherwise = x
+
+natE :: forall q. SubGroup q => E q -> Natural
+natE = unF . unE
+
+instance (SubGroup q, Additive (F (P q))) => Additive (E q) where
+	zero = E zero
+	E (F x) + E (F y) = E (F ((x + y) `mod` unF (groupOrder @q)))
+instance (SubGroup q, Negable (F (P q))) => Negable (E q) where
+	neg (E (F x)) | x == 0 = zero
+	              | otherwise = E (F (fromIntegral ( neg (toInteger x)
+	                                               + toInteger (unF (groupOrder @q)) )))
+instance (SubGroup q, Multiplicative (F (P q))) => Multiplicative (E q) where
+	one = E one
+	E (F x) * E (F y) = E (F ((x * y) `mod` unF (groupOrder @q)))
+instance SubGroup q => Random.Random (E q) where
+	randomR (E (F lo), E (F hi)) =
+		first (E . F . fromIntegral) .
+		Random.randomR
+		 ( 0`max`toInteger lo
+		 , toInteger hi`min`(toInteger (unF (groupOrder @q)) - 1) )
+	random =
+		first (E . F . fromIntegral) .
+		Random.randomR (0, toInteger (unF (groupOrder @q)) - 1)
+instance SubGroup q => Enum (E q) where
+	toEnum = inE
+	fromEnum = fromIntegral . natE
+	enumFromTo lo hi = List.unfoldr
+	 (\i -> if i<=hi then Just (i, i+one) else Nothing) lo
+
+infixr 8 ^
+-- | @(b '^' e)@ returns the modular exponentiation of base 'b' by exponent 'e'.
+(^) :: SubGroup q => G q -> E q -> G q
+(^) b (E (F e))
+ | e == zero = one
+ | otherwise = t * (b*b) ^ E (F (e`shiftR`1))
+	where
+	t | testBit e 0 = b
+		| otherwise   = one
+
+-- * Type 'RandomGen'
+type RandomGen = Random.RandomGen
+
+-- | @('randomR' i)@ returns a random integer in @[0..i-1]@.
+randomR ::
+ Monad m =>
+ RandomGen r =>
+ Random.Random i =>
+ Negable i =>
+ Multiplicative i =>
+ i -> S.StateT r m i
+randomR i = S.StateT $ return . Random.randomR (zero, i-one)
+
+-- | @('random')@ returns a random integer
+-- in the range determined by its type.
+random ::
+ Monad m =>
+ RandomGen r =>
+ Random.Random i =>
+ Negable i =>
+ Multiplicative i =>
+ S.StateT r m i
+random = S.StateT $ return . Random.random
+
+instance Random.Random Natural where
+	randomR (mini,maxi) =
+		first (fromIntegral::Integer -> Natural) .
+		Random.randomR (fromIntegral mini, fromIntegral maxi)
+	random = first (fromIntegral::Integer -> Natural) . Random.random
+
+-- * Groups
+
+-- ** Type 'WeakParams'
+-- | Weak parameters for debugging purposes only.
+data WeakParams
+instance PrimeField WeakParams where
+	fieldCharac = 263
+instance SubGroup WeakParams where
+	type P WeakParams = WeakParams
+	groupGen = G (F 2)
+	groupOrder = F 131
+
+-- ** Type 'BeleniosParams'
+-- | Parameters used in Belenios.
+-- A 2048-bit 'fieldCharac' of a 'PrimeField',
+-- with a 256-bit 'groupOrder' for a 'Multiplicative' 'SubGroup'
+-- generated by 'groupGen'.
+data BeleniosParams
+instance PrimeField BeleniosParams where
+	fieldCharac = 20694785691422546401013643657505008064922989295751104097100884787057374219242717401922237254497684338129066633138078958404960054389636289796393038773905722803605973749427671376777618898589872735865049081167099310535867780980030790491654063777173764198678527273474476341835600035698305193144284561701911000786737307333564123971732897913240474578834468260652327974647951137672658693582180046317922073668860052627186363386088796882120769432366149491002923444346373222145884100586421050242120365433561201320481118852408731077014151666200162313177169372189248078507711827842317498073276598828825169183103125680162072880719
+instance SubGroup BeleniosParams where
+	type P BeleniosParams = BeleniosParams
+	groupGen = G (F 2402352677501852209227687703532399932712287657378364916510075318787663274146353219320285676155269678799694668298749389095083896573425601900601068477164491735474137283104610458681314511781646755400527402889846139864532661215055797097162016168270312886432456663834863635782106154918419982534315189740658186868651151358576410138882215396016043228843603930989333662772848406593138406010231675095763777982665103606822406635076697764025346253773085133173495194248967754052573659049492477631475991575198775177711481490920456600205478127054728238140972518639858334115700568353695553423781475582491896050296680037745308460627)
+	groupOrder = F 78571733251071885079927659812671450121821421258408794611510081919805623223441
diff --git a/Protocol/Credential.hs b/Protocol/Credential.hs
new file mode 100644
--- /dev/null
+++ b/Protocol/Credential.hs
@@ -0,0 +1,134 @@
+module Protocol.Credential where
+
+import Control.Monad (Monad(..), replicateM)
+import Data.Bits
+import Data.Bool
+import Data.Char (Char)
+import Data.Either (Either(..))
+import Data.Eq (Eq(..))
+import Data.Function (($))
+import Data.Functor ((<$>))
+import Data.Int (Int)
+import Data.Maybe (maybe)
+import Data.Ord (Ord(..))
+import Data.Text (Text)
+import Numeric.Natural (Natural)
+import Prelude (Integral(..), fromIntegral, div)
+import Text.Show (Show)
+import qualified Control.Monad.Trans.State.Strict as S
+import qualified Crypto.KDF.PBKDF2 as Crypto
+import qualified Data.ByteArray as ByteArray
+import qualified Data.ByteString as BS
+import qualified Data.Char as Char
+import qualified Data.List as List
+import qualified Data.Text as Text
+import qualified Data.Text.Encoding as Text
+import qualified System.Random as Random
+
+import Protocol.Arithmetic
+
+-- * Type 'Credential'
+-- | A 'Credential' is a word of @('tokenLength'+1 '==' 15)@-characters
+-- from a base alphabet of (@'tokenBase' '==' 58)@ characters:
+-- "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+-- (beware the absence of "0", \"O", \"I", and "l").
+-- The last character is a checksum.
+-- The entropy is: @('tokenLength' * log 'tokenBase' / log 2) '==' 82.01… bits@.
+newtype Credential = Credential Text
+ deriving (Eq, Show)
+
+credentialAlphabet :: [Char] -- TODO: make this an array
+credentialAlphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+tokenBase :: Int
+tokenBase = List.length credentialAlphabet
+tokenLength ::Int
+tokenLength = 14
+
+-- | @'randomCredential'@ generates a random 'Credential'.
+randomCredential ::
+ Monad m =>
+ Random.RandomGen r =>
+ S.StateT r m Credential
+randomCredential = do
+	rs <- replicateM tokenLength (randomR (fromIntegral tokenBase))
+	let (tot, cs) = List.foldl' (\(acc,ds) d ->
+			( acc * tokenBase + d
+			, charOfDigit d : ds )
+		 ) (zero::Int, []) rs
+	let checksum = (neg tot + 53) `mod` 53 -- NOTE: why 53 and not 'tokenBase' ?
+	return $ Credential $ Text.reverse $ Text.pack (charOfDigit checksum:cs)
+	where
+	charOfDigit = (credentialAlphabet List.!!)
+
+-- | @'readCredential'@ reads and check the well-formedness of a 'Credential'
+-- from raw 'Text'.
+readCredential :: Text -> Either CredentialError Credential
+readCredential s
+ | Text.length s /= tokenLength + 1 = Left CredentialError_Length
+ | otherwise = do
+	tot <- Text.foldl'
+	 (\acc c -> acc >>= \a -> ((a * tokenBase) +) <$> digitOfChar c)
+	 (Right (zero::Int))
+	 (Text.init s)
+	checksum <- digitOfChar (Text.last s)
+	if (tot + checksum) `mod` 53 == 0
+	then Right (Credential s)
+	else Left CredentialError_Checksum
+	where
+	digitOfChar c =
+		maybe (Left $ CredentialError_BadChar c) Right $
+		List.elemIndex c credentialAlphabet
+
+-- ** Type 'CredentialError'
+data CredentialError
+ =   CredentialError_BadChar Char.Char
+ |   CredentialError_Checksum
+ |   CredentialError_Length
+ deriving (Eq, Show)
+
+-- ** Type 'UUID'
+newtype UUID = UUID Text
+ deriving (Eq,Ord,Show)
+
+-- | @'randomUUID'@ generates a random 'UUID'.
+randomUUID ::
+ Monad m =>
+ Random.RandomGen r =>
+ S.StateT r m UUID
+randomUUID = do
+	rs <- replicateM tokenLength (randomR (fromIntegral tokenBase))
+	let cs = List.foldl' (\ds d -> charOfDigit d : ds) [] rs
+	return $ UUID $ Text.reverse $ Text.pack cs
+	where
+	charOfDigit = (credentialAlphabet List.!!)
+
+-- ** Type 'SecretKey'
+type SecretKey = E
+
+-- | @('secretKey' uuid cred)@ returns the 'SecretKey'
+-- derived from given 'uuid' and 'cred'
+-- using 'Crypto.fastPBKDF2_SHA256'.
+secretKey :: SubGroup q => UUID -> Credential -> SecretKey q
+secretKey (UUID uuid) (Credential cred) =
+	inE $ BS.foldl'
+	 (\acc b -> acc`shiftL`3 + fromIntegral b)
+	 (0::Natural)
+	 (ByteArray.convert deriv)
+	where
+	deriv :: BS.ByteString
+	deriv =
+		Crypto.fastPBKDF2_SHA256
+		 Crypto.Parameters
+		 { Crypto.iterCounts   = 1000
+		 , Crypto.outputLength = 256 `div` 8
+		 }
+		 (Text.encodeUtf8 cred)
+		 (Text.encodeUtf8 uuid)
+
+-- ** Type 'PublicKey'
+type PublicKey = G
+
+-- | @('publicKey' secKey)@ returns the 'PublicKey'
+-- derived from given 'SecretKey'.
+publicKey :: SubGroup q => SecretKey q -> PublicKey q
+publicKey = (groupGen ^)
diff --git a/Protocol/Election.hs b/Protocol/Election.hs
new file mode 100644
--- /dev/null
+++ b/Protocol/Election.hs
@@ -0,0 +1,511 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Protocol.Election where
+
+import Control.Monad (Monad(..), mapM, zipWithM)
+import Control.Monad.Morph (MFunctor(..))
+import Control.Monad.Trans.Class (MonadTrans(..))
+import Data.Bool
+import Data.Either (either)
+import Data.Eq (Eq(..))
+import Data.Foldable (Foldable, foldMap, and)
+import Data.Function (($), id, const)
+import Data.Functor (Functor, (<$>))
+import Data.Functor.Identity (Identity(..))
+import Data.Maybe (Maybe(..), fromMaybe)
+import Data.Ord (Ord(..))
+import Data.Semigroup (Semigroup(..))
+import Data.String (IsString(..))
+import Data.Text (Text)
+import Data.Traversable (Traversable(..))
+import Data.Tuple (fst, snd)
+import GHC.Natural (minusNaturalMaybe)
+import Numeric.Natural (Natural)
+import Prelude (error, fromIntegral)
+import Text.Show (Show(..))
+import qualified Control.Monad.Trans.Except as Exn
+import qualified Control.Monad.Trans.State.Strict as S
+import qualified Data.ByteString as BS
+import qualified Data.List as List
+
+import Protocol.Arithmetic
+import Protocol.Credential
+
+-- * Type 'Encryption'
+-- | ElGamal-like encryption.
+-- Its security relies on the /Discrete Logarithm problem/.
+--
+-- Because ('groupGen' '^'encNonce '^'secKey '==' 'groupGen' '^'secKey '^'encNonce),
+-- knowing @secKey@, one can divide 'encryption_vault' by @('encryption_nonce' '^'secKey)@
+-- to decipher @('groupGen' '^'clear)@, then @clear@ must be small to be decryptable,
+-- because it is encrypted as a power of 'groupGen' to enable the additive homomorphism.
+data Encryption q = Encryption
+ { encryption_nonce :: G q
+   -- ^ Public part of the random 'encNonce': @('groupGen' '^'encNonce)@
+ , encryption_vault :: G q
+   -- ^ Encrypted clear: @('pubKey' '^'r '*' 'groupGen' '^'clear)@
+ } deriving (Eq,Show)
+
+-- | Additive homomorphism.
+-- Using the fact that: @'groupGen' '^'x '*' 'groupGen' '^'y '==' 'groupGen' '^'(x'+'y)@.
+instance SubGroup q => Additive (Encryption q) where
+	zero = Encryption one one
+	x+y = Encryption
+	 (encryption_nonce x * encryption_nonce y)
+	 (encryption_vault x * encryption_vault y)
+
+-- *** Type 'EncryptionNonce'
+type EncryptionNonce = E
+
+-- | @('encrypt' pubKey clear)@ returns an ElGamal-like 'Encryption'.
+--
+-- WARNING: the secret encryption nonce (@encNonce@)
+-- is returned alongside the 'Encryption'
+-- in order to prove the validity of the encrypted clear in 'prove',
+-- but this secret @encNonce@ MUST be forgotten after that,
+-- as it may be used to decipher the 'Encryption'
+-- without the secret key associated with 'pubKey'.
+encrypt ::
+ Monad m => RandomGen r => SubGroup q =>
+ PublicKey q -> E q ->
+ S.StateT r m (EncryptionNonce q, Encryption q)
+encrypt pubKey clear = do
+	encNonce <- random
+	-- NOTE: preserve the 'encNonce' for 'prove'.
+	return $ (encNonce,)
+		Encryption
+		 { encryption_nonce = groupGen^encNonce
+		 , encryption_vault = pubKey  ^encNonce * groupGen^clear
+		 -- NOTE: 'clear' is put as exponent in order
+		 -- to make an additive homomorphism
+		 -- instead of a multiplicative homomorphism.
+		 -- log (a*b) = log a + log b
+		 }
+
+-- * Type 'Proof'
+-- | 'Proof' of knowledge of a discrete logarithm:
+-- @secret == logBase base (base^secret)@.
+--
+-- NOTE: Since @(pubKey == 'groupGen' '^'secKey)@, then:
+-- @(logBase 'encryption_nonce' ('encryption_vault' '*' 'encryption_nonce') '==' secKey '+' clear)@.
+data Proof q = Proof
+ { proof_challenge :: Challenge q
+   -- ^ 'Challenge' sent by the verifier to the prover
+   -- to ensure that the prover really has knowledge
+   -- of the secret and is not replaying.
+   -- Actually, 'proof_challenge' is not sent in a 'prove',
+   -- but derived from the prover's 'Commitment's and statements
+   -- with a collision resistant hash.
+ , proof_response :: E q
+   -- ^ Response sent by the prover to the verifier.
+   -- Usually: @nonce '+' sec '*' 'proof_challenge'@.
+   --
+   -- To be computed efficiently, it requires @sec@:
+   -- either the @secKey@ (in 'signature_proof')
+   -- or the @encNonce@ (in 'prove').
+ } deriving (Eq,Show)
+
+-- ** Type 'Challenge'
+type Challenge = E
+
+-- ** Type 'Oracle'
+-- An 'Oracle' returns the 'Challenge' of the 'Commitment's
+-- by hashing them (eventually with other 'Commitment's).
+--
+-- Used in 'prove' it enables a Fiat-Shamir transformation
+-- of an /interactive zero-knowledge/ (IZK) proof
+-- into a /non-interactive zero-knowledge/ (NIZK) proof.
+-- That is to say that the verifier does not have
+-- to send a 'Challenge' to the prover.
+-- Indeed, the prover now handles the 'Challenge'
+-- which becomes a (collision resistant) hash
+-- of the prover's commitments (and statements to be a stronger proof).
+type Oracle list q = list (Commitment q) -> Challenge q
+
+-- | @('prove' sec commitments oracle)@
+-- returns a 'Proof' that @sec@ is known.
+--
+-- The 'Oracle' is given the 'commitments'
+-- raised to the power of the secret nonce of the 'Proof',
+-- as those are the 'commitments' that the verifier will obtain
+-- when composing the 'proof_challenge' and 'proof_response' together
+-- (in 'encryptionCommitments').
+--
+-- NOTE: 'sec' is @secKey@ in 'signature_proof' or @encNonce@ in 'proveEncryption'.
+--
+-- NOTE: The 'commitments' are @['groupGen']@ in 'signature_proof'
+-- or @['groupGen', 'pubKey']@ in 'proveEncryption'.
+--
+-- WARNING: for 'prove' to be a so-called /strong Fiat-Shamir transformation/ (not a weak):
+-- the statement must be included in the hash (not only the commitments).
+--
+-- NOTE: a 'random' @nonce@ is used to ensure each 'prove'
+-- does not reveal any information regarding the secret 'sec'.
+prove ::
+ Monad m => RandomGen r => SubGroup q => Functor list =>
+ E q -> list (Commitment q) -> Oracle list q -> S.StateT r m (Proof q)
+prove sec commitments oracle = do
+	nonce <- random
+	let proof_challenge = oracle $ (^ nonce) <$> commitments
+	return Proof
+	 { proof_challenge
+	 , proof_response = nonce - sec*proof_challenge
+	 }
+
+-- ** Type 'Commitment'
+type Commitment = G
+
+-- | @('commit' proof x y)@ returns a 'Commitment'
+-- from the given 'Proof' with the knowledge of the verifier.
+--
+-- NOTE: Contrary to Helios-C specifications,
+-- @('*')@ is used instead of @('/')@
+-- to avoid the performance cost of a modular exponentiation
+-- @('^' ('groupOrder' '-' 'one'))@,
+-- this is compensated by using @('-')@ instead of @('+')@ in 'prove'.
+commit :: SubGroup q => Proof q -> G q -> G q -> Commitment q
+commit Proof{..} x y = x^proof_response * y^proof_challenge
+{-# INLINE commit #-}
+
+-- ** Type 'Opinion'
+-- | Index of a 'Disjunction' within a list of them.
+-- It is encrypted as an 'E'xponent by 'encrypt'.
+type Opinion = E
+
+-- ** Type 'Disjunction'
+-- | A 'Disjunction' is an 'inv'ersed @('groupGen' '^'opinion)@
+-- it's used in 'proveEncryption' to generate a 'Proof'
+-- that an 'encryption_vault' contains a given @('groupGen' '^'opinion)@,
+type Disjunction = G
+
+booleanDisjunctions :: SubGroup q => [Disjunction q]
+booleanDisjunctions = List.take 2 groupGenInverses
+
+intervalDisjunctions :: SubGroup q => Opinion q -> Opinion q -> [Disjunction q]
+intervalDisjunctions mini maxi =
+	List.genericTake (fromMaybe 0 $ (natE maxi + 1)`minusNaturalMaybe`natE mini) $
+	List.genericDrop (natE mini) $
+	groupGenInverses
+
+-- ** Type 'DisjProof'
+-- | A list of 'Proof's to prove that the 'Opinion' within an 'Encryption'
+-- is indexing a 'Disjunction' within a list of them,
+-- without knowing which 'Opinion' it is.
+newtype DisjProof q = DisjProof [Proof q]
+ deriving (Eq,Show)
+
+-- | @('proveEncryption' pubKey zkp disjs opin (encNonce, enc))@
+-- returns a 'DisjProof' that 'enc' 'encrypt's
+-- one of the 'Disjunction's within 'disjs',
+-- without revealing which one it is.
+--
+-- A /NIZK Disjunctive Chaum Pedersen Logarithm Equality/ is used.
+proveEncryption ::
+ forall m r q.
+ Monad m => RandomGen r => SubGroup q =>
+ PublicKey q -> ZKP ->
+ [Disjunction q] -> Opinion q ->
+ (EncryptionNonce q, Encryption q) ->
+ S.StateT r (Exn.ExceptT ErrorProove m) (DisjProof q)
+proveEncryption pubKey zkp disjs opinion (encNonce, enc)
+ | (prevDisjs, _indexedDisj:nextDisjs) <-
+   List.genericSplitAt (natE opinion) disjs = do
+	-- Fake proofs for all values except the correct one.
+	prevFakes <- fakeProof `mapM` prevDisjs
+	nextFakes <- fakeProof `mapM` nextDisjs
+	let prevProofs = fst <$> prevFakes
+	let nextProofs = fst <$> nextFakes
+	let challengeSum =
+		sum (proof_challenge <$> prevProofs) +
+		sum (proof_challenge <$> nextProofs)
+	correctProof <- prove encNonce [groupGen, pubKey] $
+	 -- 'Oracle'
+	 \correctCommitments ->
+		let commitments =
+			foldMap snd prevFakes <>
+			correctCommitments <>
+			foldMap snd nextFakes in
+		hash (encryptionStatement zkp enc) commitments - challengeSum
+	return $ DisjProof $ prevProofs <> (correctProof : nextProofs)
+ | otherwise = lift $ Exn.throwE $
+	ErrorProove_InvalidOpinion
+	 (fromIntegral $ List.length disjs)
+	 (natE opinion)
+	where
+	fakeProof :: Disjunction q -> S.StateT r (Exn.ExceptT ErrorProove m) (Proof q, [Commitment q])
+	fakeProof disj = do
+		-- Returns 'Commitment's verifiables by the verifier,
+		-- but computed from random 'proof_challenge' and 'proof_response'
+		-- instead of correct ones.
+		proof_challenge <- random
+		proof_response  <- random
+		let proof = Proof{..}
+		return (proof, encryptionCommitments pubKey enc (disj, proof))
+
+verifyEncryption ::
+ Monad m =>
+ SubGroup q =>
+ PublicKey q -> ZKP ->
+ [Disjunction q] ->
+ (Encryption q, DisjProof q) ->
+ Exn.ExceptT ErrorValidateEncryption m Bool
+verifyEncryption pubKey zkp disjs (enc, DisjProof proofs)
+ | List.length proofs /= List.length disjs =
+	Exn.throwE $ ErrorValidateEncryption_InvalidProofLength
+	 (fromIntegral $ List.length proofs)
+	 (fromIntegral $ List.length disjs)
+ | otherwise = return $ challengeSum == hash (encryptionStatement zkp enc) commitments
+	where
+	challengeSum = sum (proof_challenge <$> proofs)
+	commitments = foldMap (encryptionCommitments pubKey enc) (List.zip disjs proofs)
+
+encryptionStatement :: SubGroup q => ZKP -> Encryption q -> BS.ByteString
+encryptionStatement (ZKP zkp) Encryption{..} =
+	"prove|"<>zkp<>"|"<>
+	fromString (show (natG encryption_nonce))<>","<>
+	fromString (show (natG encryption_vault))<>"|"
+
+-- | @('encryptionCommitments' pubKey enc (disj,proof))@
+-- returns the 'Commitment's with only the knowledge of the verifier.
+--
+-- The 'Proof' comes from 'prove' of @fakeProof@ in 'proveEncryption'.
+encryptionCommitments ::
+ SubGroup q =>
+ PublicKey q -> Encryption q ->
+ (Disjunction q, Proof q) -> [G q]
+encryptionCommitments pubKey Encryption{..} (disj, proof) =
+	[ commit proof groupGen encryption_nonce
+	  -- == groupGen ^ nonce if 'Proof' comes from 'prove'
+	, commit proof pubKey (encryption_vault*disj)
+	  -- == pubKey ^ nonce if 'Proof' comes from 'prove'
+	  -- and 'encryption_vault' encrypts (- logBase groupGen disj).
+	]
+
+-- ** Type 'ZKP'
+-- | Zero-knowledge proof
+newtype ZKP = ZKP BS.ByteString
+
+-- ** Type 'ErrorProove'
+-- | Error raised by 'proveEncryption'.
+data ErrorProove
+ =   ErrorProove_InvalidOpinion Natural Natural
+     -- ^ When the opinion is not within the number of 'Disjunction's.
+ deriving (Eq,Show)
+
+-- ** Type 'ErrorValidateEncryption'
+-- | Error raised by 'verifyEncryption'.
+data ErrorValidateEncryption
+ =   ErrorValidateEncryption_InvalidProofLength Natural Natural
+     -- ^ When the number of proofs is different than
+     -- the number of 'Disjunction's.
+ deriving (Eq,Show)
+
+-- * Type 'Question'
+data Question q = Question
+ { question_text    :: Text
+ , question_choices :: [Text]
+ , question_mini    :: Opinion q
+ , question_maxi    :: Opinion q
+ -- , question_blank :: Maybe Bool
+ } deriving (Eq, Show)
+
+-- * Type 'Answer'
+data Answer q = Answer
+ { answer_opinions :: [(Encryption q, DisjProof q)]
+   -- ^ Encrypted 'Opinion' for each 'question_choices'
+   -- with a 'DisjProof' that they belong to [0,1].
+ , answer_sumProof :: DisjProof q
+   -- ^ Proofs that the sum of the 'Opinon's encrypted in 'answer_opinions'
+   -- is an element of @[mini..maxi]@.
+ -- , answer_blankProof ::
+ } deriving (Eq,Show)
+
+-- ** Type 'ErrorAnswer'
+-- | Error raised by 'encryptAnswer'.
+data ErrorAnswer
+ =   ErrorAnswer_WrongNumberOfOpinions Natural Natural
+     -- ^ When the number of opinions is different than
+     -- the number of choices ('question_choices').
+ |   ErrorAnswer_WrongSumOfOpinions Natural Natural Natural
+     -- ^ When the sum of opinions is not within the bounds
+     -- of 'question_mini' and 'question_maxi'.
+ deriving (Eq,Show)
+
+-- | @('encryptAnswer' pubKey zkp quest opinions)@
+-- returns an 'Answer' validable by 'verifyAnswer',
+-- unless an 'ErrorAnswer' is returned.
+encryptAnswer ::
+ Monad m => RandomGen r => SubGroup q =>
+ PublicKey q -> ZKP ->
+ Question q -> [Bool] ->
+ S.StateT r (Exn.ExceptT ErrorAnswer m) (Answer q)
+encryptAnswer pubKey zkp Question{..} opinionsBools
+ | not (question_mini <= opinionsSum && opinionsSum <= question_maxi) =
+	lift $ Exn.throwE $
+		ErrorAnswer_WrongSumOfOpinions
+		 (natE opinionsSum)
+		 (natE question_mini)
+		 (natE question_maxi)
+ | List.length opinions /= List.length question_choices =
+	lift $ Exn.throwE $
+		ErrorAnswer_WrongNumberOfOpinions
+		 (fromIntegral $ List.length opinions)
+		 (fromIntegral $ List.length question_choices)
+ | otherwise = do
+	encryptions <- encrypt pubKey `mapM` opinions
+	hoist (Exn.withExceptT (\case
+		 ErrorProove_InvalidOpinion{} -> error "encryptAnswer: impossible happened"
+	 )) $ do
+		individualProofs <- zipWithM
+		 (proveEncryption pubKey zkp booleanDisjunctions)
+		 opinions encryptions
+		sumProof <- proveEncryption pubKey zkp
+		 (intervalDisjunctions question_mini question_maxi)
+		 (opinionsSum - question_mini)
+		 ( sum (fst <$> encryptions) -- NOTE: sum the 'encNonce's
+		 , sum (snd <$> encryptions) -- NOTE: sum the 'Encryption's
+		 )
+		return $ Answer
+		 { answer_opinions = List.zip
+			 (snd <$> encryptions) -- NOTE: drop encNonce
+			 individualProofs
+		 , answer_sumProof = sumProof
+		 }
+ where
+	opinionsSum = sum opinions
+	opinions = (\o -> if o then one else zero) <$> opinionsBools
+
+verifyAnswer ::
+ SubGroup q =>
+ PublicKey q -> ZKP ->
+ Question q -> Answer q -> Bool
+verifyAnswer pubKey zkp Question{..} Answer{..}
+ | List.length question_choices /= List.length answer_opinions = False
+ | otherwise = either (const False) id $ Exn.runExcept $ do
+	validOpinions <-
+		verifyEncryption pubKey zkp booleanDisjunctions
+		 `traverse` answer_opinions
+	validSum <- verifyEncryption pubKey zkp
+	 (intervalDisjunctions question_mini question_maxi)
+	 ( sum (fst <$> answer_opinions)
+	 , answer_sumProof )
+	return (and validOpinions && validSum)
+
+-- * Type 'Election'
+data Election q = Election
+ { election_name        :: Text
+ , election_description :: Text
+ , election_publicKey   :: PublicKey q
+ , election_questions   :: [Question q]
+ , election_uuid        :: UUID
+ , election_hash        :: Hash -- TODO: serialize to JSON to calculate this
+ } deriving (Eq,Show)
+
+-- ** Type 'Hash'
+newtype Hash = Hash Text
+ deriving (Eq,Ord,Show)
+
+-- * Type 'Ballot'
+data Ballot q = Ballot
+ { ballot_answers       :: [Answer q]
+ , ballot_signature     :: Maybe (Signature q)
+ , ballot_election_uuid :: UUID
+ , ballot_election_hash :: Hash
+ }
+
+-- | @('encryptBallot' elec ('Just' secKey) opinionsByQuest)@
+-- returns a 'Ballot' signed by 'secKey' (the voter's secret key)
+-- where 'opinionsByQuest' is a list of 'Opinion's
+-- on each 'question_choices' of each 'election_questions'.
+encryptBallot ::
+ Monad m => RandomGen r => SubGroup q =>
+ Election q -> Maybe (SecretKey q) -> [[Bool]] ->
+ S.StateT r (Exn.ExceptT ErrorBallot m) (Ballot q)
+encryptBallot Election{..} secKeyMay opinionsByQuest
+ | List.length election_questions /= List.length opinionsByQuest =
+	lift $ Exn.throwE $
+		ErrorBallot_WrongNumberOfAnswers
+		 (fromIntegral $ List.length opinionsByQuest)
+		 (fromIntegral $ List.length election_questions)
+ | otherwise = do
+	let (keysMay, zkp) =
+		case secKeyMay of
+		 Nothing -> (Nothing, ZKP "")
+		 Just secKey ->
+			( Just (secKey, pubKey)
+			, ZKP (fromString (show (natG pubKey))) )
+			where pubKey = groupGen ^ secKey
+	ballot_answers <-
+		hoist (Exn.withExceptT ErrorBallot_Answer) $
+			zipWithM (encryptAnswer election_publicKey zkp)
+			 election_questions opinionsByQuest
+	ballot_signature <- case keysMay of
+	 Nothing -> return Nothing
+	 Just (secKey, signature_publicKey) -> do
+		signature_proof <-
+			prove secKey (Identity groupGen) $
+			 \(Identity commitment) ->
+				hash
+				 (signatureCommitments zkp commitment)
+				 (signatureStatement ballot_answers)
+		return $ Just Signature{..}
+	return Ballot
+	 { ballot_answers
+	 , ballot_election_hash = election_hash
+	 , ballot_election_uuid = election_uuid
+	 , ballot_signature
+	 }
+
+verifyBallot :: SubGroup q => Election q -> Ballot q -> Bool
+verifyBallot Election{..} Ballot{..} =
+	ballot_election_uuid == election_uuid &&
+	ballot_election_hash == election_hash &&
+	List.length election_questions == List.length ballot_answers &&
+	let (isValidSign, zkpSign) =
+		case ballot_signature of
+		 Nothing -> (True, ZKP "")
+		 Just Signature{..} ->
+			let zkp = ZKP (fromString (show (natG signature_publicKey))) in
+			(, zkp) $
+				proof_challenge signature_proof == hash
+				 (signatureCommitments zkp (commit signature_proof groupGen signature_publicKey))
+				 (signatureStatement ballot_answers)
+	in
+	and $ isValidSign :
+		List.zipWith (verifyAnswer election_publicKey zkpSign)
+		 election_questions ballot_answers
+
+-- ** Type 'Signature'
+-- | Schnorr-like signature.
+--
+-- Used to avoid 'Ballot' stuffing.
+data Signature q = Signature
+ { signature_publicKey :: PublicKey q
+ , signature_proof     :: Proof q
+ }
+
+-- | @('signatureStatement' answers)@
+-- returns all the 'encryption_nonce's and 'encryption_vault's
+-- of the given @answers@.
+signatureStatement :: Foldable f => SubGroup q => f (Answer q) -> [G q]
+signatureStatement =
+	foldMap $ \Answer{..} ->
+		(`foldMap` answer_opinions) $ \(Encryption{..}, _proof) ->
+			[encryption_nonce, encryption_vault]
+
+-- | @('signatureCommitments' zkp commitment)@
+-- returns the hashable content from the knowledge of the verifier.
+signatureCommitments :: SubGroup q => ZKP -> Commitment q -> BS.ByteString
+signatureCommitments (ZKP zkp) commitment =
+	"sig|"<>zkp<>"|"<>fromString (show (natG commitment))<>"|"
+
+-- ** Type 'ErrorBallot'
+-- | Error raised by 'encryptBallot'.
+data ErrorBallot
+ =   ErrorBallot_WrongNumberOfAnswers Natural Natural
+     -- ^ When the number of answers
+     -- is different than the number of questions.
+ |   ErrorBallot_Answer ErrorAnswer
+     -- ^ When 'encryptAnswer' raised an 'ErrorAnswer'.
+ deriving (Eq,Show)
diff --git a/hjugement-protocol.cabal b/hjugement-protocol.cabal
new file mode 100644
--- /dev/null
+++ b/hjugement-protocol.cabal
@@ -0,0 +1,201 @@
+name: hjugement-protocol
+-- PVP:  +-+------- breaking API changes
+--       | | +----- non-breaking API additions
+--       | | | +--- code changes with no API change
+version: 0.0.0.20190428
+category: Politic
+synopsis: A cryptographic protocol for the Majority Judgment.
+description:
+  This work-in-progress library aims at implementing an online voting protocol
+  named <https://eprint.iacr.org/2013/177.pdf Helios-C> (Helios with Credentials)
+  by its authors from the <https://www.cnrs.fr/ CNRS>,
+  the <http://www.loria.fr INRIA>
+  and the <https://www.univ-lorraine.fr/ Université de Lorraine>:
+  <http://www.loria.fr/~cortier/ Véronique Cortier>,
+  <https://dgalindo.es/ David Galindo>,
+  <http://www.loria.fr/~gaudry/ Pierrick Gaudry>,
+  <http://stephane.glondu.net/ Stéphane Glondu>
+  and Malika Izabachène.
+  .
+  (TODO) Actually, this protocol is adapted a little bit here to better support
+  a better method of voting known as the <http://libgen.io/book/index.php?md5=BF67AA4298C1CE7633187546AA53E01D Majority Judgment>.
+  .
+  A large-public introduction (in french) to Helios-C is available here:
+  <https://members.loria.fr/VCortier/files/Papers/Bulletin1024-2016.pdf Bulletin de la société informatique de France – numéro 9, novembre 2016>.
+  .
+  The main properties of this protocol are:
+  .
+  * /fully correct/: the published result are proven to correspond
+    to the (sum of) intended votes of the voters,
+    while accounting for a malicious bulletin board (BB) (adding fake ballots)
+    by requiring a registration authority (RA)
+    (responsible for generating and sending voters' credentials).
+    Assuming that the BB and the RA are not simultaneously dishonest.
+  .
+  * /verifiable/: each voter is able to check that:
+    his\/her ballot did contribute to the outcome (/individual verifiability/),
+    and that the tallying authorities did their job properly (/universal verifiability/).
+  .
+  * /private/: the identities of the voters who cast a vote are not publicly revealed.
+  .
+  More specifically, in this protocol :
+  .
+  * Ballots are encrypted using public-key cryptography
+    secured by the /Discrete Logarithm problem/:
+    finding @x@ in @g^x `mod` p@, where @p@ is a large prime
+    and @g@ a generator of @Gq@, the multiplicative subgroup of order @q@,
+    in @Fp@ (the finite prime field whose characteristic is @p@).
+    Here, @p@ is 2048-bit and @q@ is 256-bit.
+    The signing (Schnorr-like), the encrypting (ElGamal-like)
+    and the /Decisional Diffe Hellman/ (DDH) assumption,
+    all rely on the hardness of that problem.
+  * Ballots are added without being decrypted
+    because adding (multiplying actually) ciphertexts then decrypting,
+    is like decrypting then adding plaintexts (/additive homomorphism/).
+    Which requires to solve the /Discrete Logarithm Problem/
+    for numbers in the order of the number of voters,
+    which is not hard for small numbers (with a lookup table as here,
+    or with Pollard’s rho algorithm for logarithms).
+  * The /Schnorr protocol/ is used to prove that a voter has knowledge
+    of the secret key used to sign their votes.
+    A voter's credentials is a secret key (the signing key)
+    that has a public part (the verification key).
+    The association between the public part and the corresponding voter’s identity
+    does not need to be known, and actually should not be disclosed to satisfy
+    e.g. the French requirements regarding voting systems.
+    Using credentials prevent the submission of duplicated ballots
+    (because they are added as an additional input to the random oracle
+    in the /non-interactive zero-knowledge/ (NIZK) proofs for ciphertext well-formedness).
+    This allows a testing of duplicates which depends only on the size of the number of voters,
+    and thus enables Helios-C to scale for larger elections while attaining correctness.
+  * The /Chaum-Pedersen protocol/ (proving that equality of discrete logarithms)
+    is used to prove that ciphertexts are well-formed
+    (encrypting a 0 or a 1… or any expected natural) without decrypting them.
+    Which is known as a /Disjunctive Chaum-Pedersen/ proof of partial knowledge.
+  * A /strong Fiat-Shamir transformation/ is used
+    to transform the /interactive zero-knowledge/ (IZK) /Chaum-Pedersen protocol/
+    into a /non-interactive zero-knowledge/ (NIZK) proof, using a SHA256 hash.
+  * (TODO) A Pedersen's /distributed key generation/ (DKG) protocol
+    coupled with ElGamal keys (under the DDH assumption),
+    is used to have a fully distributed semantically secure encryption.
+extra-doc-files: 
+license: GPL-3
+license-file: COPYING
+stability: experimental
+author:      Julien Moutinho <julm+hjugement@autogeree.net>
+maintainer:  Julien Moutinho <julm+hjugement@autogeree.net>
+bug-reports: Julien Moutinho <julm+hjugement@autogeree.net>
+-- homepage:
+
+build-type: Simple
+cabal-version: 1.24
+tested-with: GHC==8.4.4
+extra-source-files:
+  stack.yaml
+extra-tmp-files:
+
+Source-Repository head
+ location: git://git.autogeree.net/hjugement
+ type:     git
+
+Library
+  exposed-modules:
+    Protocol.Arithmetic
+    Protocol.Credential
+    Protocol.Election
+  default-language: Haskell2010
+  default-extensions:
+    AllowAmbiguousTypes
+    ConstraintKinds
+    DefaultSignatures
+    FlexibleContexts
+    FlexibleInstances
+    GeneralizedNewtypeDeriving
+    LambdaCase
+    MonoLocalBinds
+    MultiParamTypeClasses
+    NamedFieldPuns
+    NoImplicitPrelude
+    NoMonomorphismRestriction
+    RecordWildCards
+    ScopedTypeVariables
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeOperators
+    UndecidableInstances
+  ghc-options:
+    -Wall
+    -Wincomplete-uni-patterns
+    -Wincomplete-record-updates
+    -fno-warn-tabs
+    -- -fhide-source-paths
+  build-depends:
+      base >= 4.6 && < 5
+    , bytestring >= 0.10
+    , containers >= 0.5
+    , cryptonite >= 0.25
+    -- , fixed-vector >= 1.1
+    -- , hashable >= 1.2.6
+    , memory >= 0.14
+    , mmorph >= 1.1
+    -- , monad-classes >= 0.3
+    , random >= 1.1
+    -- , reflection >= 2.1
+    , text >= 1.2
+    , transformers >= 0.5
+    , unordered-containers >= 0.2.8
+
+Test-Suite hjugement-protocol-test
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Main.hs
+  other-modules:
+    HUnit
+    HUnit.Arithmetic
+    HUnit.Credential
+    HUnit.Election
+    HUnit.Utils
+    -- QuickCheck
+  default-language: Haskell2010
+  default-extensions:
+    AllowAmbiguousTypes
+    ConstraintKinds
+    DefaultSignatures
+    FlexibleContexts
+    FlexibleInstances
+    GeneralizedNewtypeDeriving
+    LambdaCase
+    MonoLocalBinds
+    MultiParamTypeClasses
+    NamedFieldPuns
+    NoImplicitPrelude
+    NoMonomorphismRestriction
+    RecordWildCards
+    ScopedTypeVariables
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeOperators
+    UndecidableInstances
+  ghc-options:
+    -Wall
+    -Wincomplete-uni-patterns
+    -Wincomplete-record-updates
+    -fno-warn-tabs
+    -- -fhide-source-paths
+  build-depends:
+      hjugement-protocol
+    , base >= 4.6 && < 5
+    , containers >= 0.5
+    , hashable >= 1.2.6
+    , QuickCheck >= 2.0
+    -- , monad-classes >= 0.3
+    , random >= 1.1
+    -- , reflection >= 2.1
+    , tasty >= 0.11
+    , tasty-hunit >= 0.9
+    , tasty-quickcheck
+    , text >= 1.2
+    , transformers >= 0.5
+    , unordered-containers >= 0.2.8
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,5 @@
+resolver: lts-12.26
+packages:
+- '.'
+- location: '../hjugement'
+  extra-dep: true
diff --git a/test/HUnit.hs b/test/HUnit.hs
new file mode 100644
--- /dev/null
+++ b/test/HUnit.hs
@@ -0,0 +1,13 @@
+module HUnit where
+import Test.Tasty
+import qualified HUnit.Arithmetic
+import qualified HUnit.Credential
+import qualified HUnit.Election
+
+hunits :: TestTree
+hunits =
+	testGroup "HUnit"
+	 [ HUnit.Arithmetic.hunit
+	 , HUnit.Credential.hunit
+	 , HUnit.Election.hunit
+	 ]
diff --git a/test/HUnit/Arithmetic.hs b/test/HUnit/Arithmetic.hs
new file mode 100644
--- /dev/null
+++ b/test/HUnit/Arithmetic.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE OverloadedStrings #-}
+module HUnit.Arithmetic where
+
+import Protocol.Arithmetic
+import HUnit.Utils
+
+hunit :: TestTree
+hunit = testGroup "Arithmetic"
+ [ testGroup "inv"
+	 [ testGroup "WeakParams"
+		 [ testCase "groupGen" $
+			inv (groupGen @WeakParams) @?=
+				groupGen ^ E (groupOrder @WeakParams + neg one)
+		 ]
+	 , testGroup "BeleniosParams"
+		 [ testCase "groupGen" $
+			inv (groupGen @BeleniosParams) @?=
+				groupGen ^ E (groupOrder @BeleniosParams + neg one)
+		 ]
+	 ]
+ , testGroup "hash"
+	 [ testGroup "WeakParams"
+		 [ testCase "[groupGen]" $
+			hash "start" [groupGen @WeakParams] @?= inE 80
+		 , testCase "[groupGen, groupGen]" $
+			hash "start" [groupGen @WeakParams, groupGen] @?= inE 117
+		 ]
+	 , testGroup "BeleniosParams"
+		 [ testCase "[groupGen]" $
+			hash "start" [groupGen @BeleniosParams] @?=
+				inE 1115773133278002110129249165266
+		 , testCase "[groupGen, groupGen]" $
+			hash "start" [groupGen @BeleniosParams, groupGen] @?=
+				inE 1237765159213600087872608890753
+		 ]
+	 ]
+ ]
diff --git a/test/HUnit/Credential.hs b/test/HUnit/Credential.hs
new file mode 100644
--- /dev/null
+++ b/test/HUnit/Credential.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE OverloadedStrings #-}
+module HUnit.Credential where
+
+import Control.Applicative (Applicative(..))
+import qualified Control.Monad.Trans.State.Strict as S
+import qualified System.Random as Random
+
+import Protocol.Arithmetic
+import Protocol.Credential
+import HUnit.Utils
+
+hunit :: TestTree
+hunit = testGroup "Credential"
+ [ testGroup "randomCredential"
+	 [ testCase "0" $
+		S.evalState randomCredential (Random.mkStdGen 0) @?=
+			Credential "xLcs7ev6Jy6FHHE"
+	 ]
+ , testGroup "randomUUID"
+	 [ testCase "0" $
+		S.evalState randomUUID (Random.mkStdGen 0) @?=
+			UUID "xLcs7ev6Jy6FHH"
+	 ]
+ , testGroup "readCredential" $
+		let (==>) inp exp =
+			testCase (show inp) $ readCredential inp @?= exp in
+	 [ "" ==> Left CredentialError_Length
+	 , "xLcs7ev6Jy6FH_E"  ==> Left (CredentialError_BadChar '_')
+	 , "xLcs7ev6Jy6FHIE"  ==> Left (CredentialError_BadChar 'I')
+	 , "xLcs7ev6Jy6FH0E"  ==> Left (CredentialError_BadChar '0')
+	 , "xLcs7ev6Jy6FHOE"  ==> Left (CredentialError_BadChar 'O')
+	 , "xLcs7ev6Jy6FHlE"  ==> Left (CredentialError_BadChar 'l')
+	 , "xLcs7ev6Jy6FH6"   ==> Left CredentialError_Length
+	 , "xLcs7ev6Jy6FHHy1" ==> Left CredentialError_Length
+	 , "xLcs7ev6Jy6FHHF"  ==> Left CredentialError_Checksum
+	 , "xLcs7ev6Jy6FHHE"  ==> Right (Credential "xLcs7ev6Jy6FHHE")
+	 ]
+ , testGroup "secretKey" $
+	 [ testSecretKey @WeakParams 0 $ E (F 122)
+	 , testSecretKey @WeakParams 1 $ E (F 35)
+	 , testSecretKey @BeleniosParams 0 $ E (F 2317630607062989137269685509390)
+	 , testSecretKey @BeleniosParams 1 $ E (F 1968146140481358915910346867611)
+	 ]
+ ]
+
+testSecretKey :: forall q. SubGroup q => Int -> E q -> TestTree
+testSecretKey seed exp =
+	let (uuid@(UUID u), cred@(Credential c)) =
+		(`S.evalState` Random.mkStdGen seed) $
+			(,) <$> randomUUID <*> randomCredential in
+	testCase (show (u,c)) $
+		secretKey @q uuid cred @?= exp
diff --git a/test/HUnit/Election.hs b/test/HUnit/Election.hs
new file mode 100644
--- /dev/null
+++ b/test/HUnit/Election.hs
@@ -0,0 +1,111 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
+module HUnit.Election where
+
+-- import Control.Applicative (Applicative(..))
+import qualified Control.Monad.Trans.Except as Exn
+import qualified Control.Monad.Trans.State.Strict as S
+import qualified Data.List as List
+import qualified System.Random as Random
+
+import Protocol.Arithmetic
+import Protocol.Credential
+import Protocol.Election
+import HUnit.Utils
+
+-- * Type 'Params'
+class SubGroup q => Params q where
+	paramsName :: String
+instance Params WeakParams where
+	paramsName = "WeakParams"
+instance Params BeleniosParams where
+	paramsName = "BeleniosParams"
+
+hunit :: TestTree
+hunit = testGroup "Election"
+ [ testGroup "groupGenInverses"
+	 [ testCase "WeakParams" $
+		List.take 10 (groupGenInverses @WeakParams) @?=
+			[groupGen^neg (inE i) | i <- [0..9::Int]]
+	 , testCase "BeleniosParams" $
+		List.take 10 (groupGenInverses @BeleniosParams) @?=
+			[groupGen^neg (inE i) | i <- [0..9::Int]]
+	 ]
+ , testGroup "encryptBallot" $
+	 [ testsEncryptBallot @WeakParams
+	 , testsEncryptBallot @BeleniosParams
+	 ]
+ ]
+
+testsEncryptBallot :: forall q. Params q => TestTree
+testsEncryptBallot =
+	testGroup (paramsName @q)
+	 [ testEncryptBallot @q 0
+		 [Question "q1" ["a1","a2","a3"] zero one]
+		 [[True, False, False]]
+		 (Right True)
+	 , testEncryptBallot @q 0
+		 [Question "q1" ["a1","a2","a3"] zero one]
+		 [[False, False, False]]
+		 (Right True)
+	 , testEncryptBallot @q 0
+		 [Question "q1" ["a1","a2","a3"] zero one]
+		 [[False, False, False]]
+		 (Right True)
+	 , testEncryptBallot @q 0
+		 [Question "q1" [] zero one]
+		 []
+		 (Left (ErrorBallot_WrongNumberOfAnswers 0 1))
+	 , testEncryptBallot @q 0
+		 [Question "q1" ["a1","a2"] one one]
+		 [[True]]
+		 (Left (ErrorBallot_Answer (ErrorAnswer_WrongNumberOfOpinions 1 2)))
+	 , testEncryptBallot @q 0
+		 [Question "q1" ["a1","a2","a3"] zero one]
+		 [[True, True, False]]
+		 (Left (ErrorBallot_Answer (ErrorAnswer_WrongSumOfOpinions 2 0 1)))
+	 , testEncryptBallot @q 0
+		 [Question "q1" ["a1","a2","a3"] one one]
+		 [[False, False, False]]
+		 (Left (ErrorBallot_Answer (ErrorAnswer_WrongSumOfOpinions 0 1 1)))
+	 , testEncryptBallot @q 0
+		 [Question "q1" ["a1","a2"] one one]
+		 [[False, False, True]]
+		 (Left (ErrorBallot_Answer (ErrorAnswer_WrongNumberOfOpinions 3 2)))
+	 , testEncryptBallot @q 0
+		 [ Question "q1" ["a11","a12","a13"] zero (one+one)
+		 , Question "q2" ["a21","a22","a23"] one one
+		 ]
+		 [ [True, False, True]
+		 , [False, True, False] ]
+		 (Right True)
+	 ]
+
+testEncryptBallot ::
+ forall q. SubGroup q =>
+ Int -> [Question q] -> [[Bool]] ->
+ Either ErrorBallot Bool ->
+ TestTree
+testEncryptBallot seed quests opins exp =
+	let verify =
+		Exn.runExcept $
+		(`S.evalStateT` Random.mkStdGen seed) $ do
+			uuid <- randomUUID
+			cred <- randomCredential
+			let secKey = secretKey @q uuid cred
+			let pubKey = publicKey secKey
+			let elec = Election
+				 { election_name        = "election"
+				 , election_description = "description"
+				 , election_publicKey   = pubKey
+				 , election_questions   = quests
+				 , election_uuid        = uuid
+				 , election_hash        = Hash ""
+				 }
+			verifyBallot elec
+			 <$> encryptBallot elec (Just secKey) opins
+	in
+	testCase (show opins) $
+		verify @?= exp
diff --git a/test/HUnit/Utils.hs b/test/HUnit/Utils.hs
new file mode 100644
--- /dev/null
+++ b/test/HUnit/Utils.hs
@@ -0,0 +1,37 @@
+module HUnit.Utils
+ ( module Test.Tasty
+ , module Test.Tasty.HUnit
+ , Monad(..), replicateM, when
+ , module Data.Bool
+ , Eq(..)
+ , Either(..)
+ , ($), (.)
+ , (<$>)
+ , Int
+ , Maybe(..)
+ , Monoid(..)
+ , Ord(..)
+ , String
+ , Text
+ , Word8
+ , Num, Fractional(..), Integral(..), Integer, undefined, fromIntegral
+ , Show(..)
+ ) where
+
+import Control.Monad (Monad(..), replicateM, when)
+import Data.Bool
+import Data.Either (Either(..))
+import Data.Eq (Eq(..))
+import Data.Function (($), (.))
+import Data.Functor ((<$>))
+import Data.Int (Int)
+import Data.Maybe (Maybe(..))
+import Data.Monoid (Monoid(..))
+import Data.Ord (Ord(..))
+import Data.String (String)
+import Data.Text (Text)
+import Data.Word (Word8)
+import Prelude (Num, Fractional(..), Integral(..), Integer, undefined, fromIntegral)
+import Test.Tasty
+import Test.Tasty.HUnit
+import Text.Show (Show(..))
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,15 @@
+module Main where
+
+import System.IO (IO)
+import Data.Function (($))
+import Test.Tasty
+-- import QuickCheck
+import HUnit
+
+main :: IO ()
+main =
+	defaultMain $
+	testGroup "Protocol"
+	 [ hunits
+	 -- , quickchecks
+	 ]
