\documentclass[]{article}
\usepackage{amsmath,graphicx,supertabular,hyperref,tabularx,ifthen}
\title{TIP \#28: How to be a good maintainer for Tcl/Tk}
\date{February 23, 2001}
\author{Don Porter}
\urlstyle{sf}
\setlength{\parskip}{1ex}
\setlength{\parindent}{0pt}
\def\tipversion$#1${\texttt{\$#1\$}}
\def\tiplangle#1{\ensuremath{<}}
\def\tiprangle#1{\ensuremath{>}}
\def\tipbar#1{\ensuremath{|}}
\def\tipmail#1#2{\(\langle\){\small\expandafter\url{#1@#2}}\(\rangle\)}
\ifx\pdfoutput\undefined
\newcommand{\tipimage}[2]{\typeout{Make sure you download #1.eps}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.eps}}{\includegraphics[width=0.8\textwidth]{#1.eps}}}
\newcommand{\tipxref}[1]{}
\newcommand{\tipxrefend}{}
\else
\newcommand{\tipimage}[2]{\typeout{Make sure you create #1.pdf}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.pdf}}{\includegraphics[width=0.8\textwidth]{#1.pdf}}}
\pdfcatalog{/PageMode /UseOutlines}
\newcommand{\tipxref}[1]{\pdfannotlink attr {/C [0.5 0.5 1.0] /Border [0 0 1]} goto name {#1}}
\newcommand{\tipxrefend}{\pdfendlink}
\fi
\newenvironment{tipabstract}{\begin{abstract}}{\end{abstract}}
\begin{document}\maketitle
\begin{center}\begin{tabularx}{\linewidth}{|r@{: }X|}\hline
\textbf{TIP \#28}&\textbf{How to be a good maintainer for Tcl/Tk}\\\hline
Author&

Don Porter \tipmail{dgp}{users.sourceforge.net}
\\
Created&Friday, \(\text{23}^{\text{rd}}\) February 2001\\
Type&Informative\\
State&Draft\\
Vote&Pending\\
Version&\tipversion$Revision: 1.24 $\\
Post History&\\
\hline\end{tabularx}\end{center}
\thispagestyle{empty}\pagestyle{empty}
\begin{tipabstract}

This document presents information and advice to maintainers in the form of a Frequently Asked Questions (FAQ) list.
\end{tipabstract}
\tableofcontents\setcounter{page}{0}\clearpage\pagestyle{plain}

\section{Preface}

Notice in the header above that this is a Draft document. It won't be the \textit{official} word of the TCT unless/until it is accepted by the TCT. Meanwhile, it should still be a helpful guide to those serving or considering service as maintainers. At the very least it's a useful straw man to revise into something better. Help us make it even more useful by using the [Edit] link at the bottom of this page (if any) to add/revise the questions and answers, or add your comments.

\section{Background}

TCT procedures (see \cite{tip0}) calls for one or more \textit{maintainers} to take responsibility for each functional area of the Tcl (\cite{tip16}) or Tk (\cite{tip23}) source code. Every source code patch to Tcl or Tk will be committed to the official branches of the appropriate CVS repository only after approval by an appropriate set of maintainers.

\section{Can I be a Tcl/Tk maintainer?}

Most likely. To be a maintainer, you should have...
\begin{itemize}
\item{}
...an interest in Tcl/Tk.
\item{}
...access to the Internet (Web and e-mail).
\item{}
...some volunteer time to contribute.
\item{}
...the ability and the support software to code in C and/or Tcl, use CVS, use SourceForge facilities, and familiarity with a portion of the Tcl/Tk source code to be maintained, or the willingness to acquire these things.
\end{itemize}

For the most part, if you are reading this document, you probably have what it takes to be a Tcl/Tk maintainer.

\section{What can I maintain?}

The Tcl Core Team (TCT) has divided up the Tcl/Tk source code into functional areas as described in \cite{tip16} and \cite{tip23}. You can volunteer to help maintain as many areas as you think you can handle. Select those you have experience with or an interest in.

\section{What does a maintainer do?}

Maintainers are the people who make changes to the files that make up the source code distribution of Tcl or Tk -{}- code, documentation, and tests. That's what a maintainer does: check in changes to the official source code in the area he/she maintains.

The source code can be changed for several reasons: to correct a bug, to add a new feature, or to re-implement an existing feature in a new way. The reason for a change controls how much oversight the maintainer must have while making the change. More on this below.

\section{How do I prepare to be a maintainer?}

The official repositories of Tcl and Tk source code are kept at SourceForge, so you need to register for a SourceForge account [\url{https://sourceforge.net/account/register.php}]. As part of the registration, you will select a login name. When you volunteer as a maintainer, the administrators of the Tcl or Tk projects will need that name to give you write access to the appropriate repository.

Once you have a SourceForge account, get familiar with the tools it provides. Most important is that you get set up to use CVS over SSH to access the repository. This can be difficult. There are some notes [\url{http://tcltk.org/sourceforge}] on how other Developers on the Tcl and Tk projects have been able to successfully get this done.

This document does not include instructions on how to use CVS. See the following references for assistance with learning CVS.
\begin{itemize}
\item{}
\url{http://cvsbook.red-bean.com/cvsbook.html}
\end{itemize}

\textit{Add more references here please.}

\section{How do I volunteer to be a maintainer?}

Send a message to \tipmail{tcl-core}{lists.sourceforge.net} telling the TCT your SourceForge login name and what area(s) you want to help maintain. Someone will add you to the list of \textit{Developers} on the Tcl or Tk projects and enable your access to SourceForge features like the Bug Tracker and Patch Manager. As a Developer, you will have write access to the appropriate repository of official source code.

\section{Write access! So I can just start changing Tcl/Tk?!}

For some purposes, yes. For others, you'll need to get approval from the TCT first. Read on...

\section{What Internet resources does a maintainer use?}

A maintainer uses the SourceForge Bug Tracker for Tcl or Tk to learn what bugs are reported in his area (browse by Category).
\begin{itemize}
\item{}
\url{http://sourceforge.net/bugs/?group_id=10894}
\item{}
\url{http://sourceforge.net/bugs/?group_id=12997}
\end{itemize}

A maintainer uses the SourceForge Patch Manager for Tcl or Tk to learn what patches make changes in his area (browse by Category).
\begin{itemize}
\item{}
\url{http://sourceforge.net/patch/?group_id=10894}
\item{}
\url{http://sourceforge.net/patch/?group_id=12997}
\end{itemize}

A maintainer uses CVS via SSH to access, track, and modify the various branches of development in the repository of official Tcl or Tk source code.
\begingroup\small\begin{verbatim}
cvs -d :ext:username@tcl.cvs.sourceforge.net:/cvsroot/tcl \
   checkout -r $BRANCH_TAG -d $LOCAL_DIR tcl

cvs -d :ext:username@tktoolkit.cvs.sourceforge.net:/cvsroot/tktoolkit \
   checkout -r $BRANCH_TAG -d $LOCAL_DIR tk
\end{verbatim}
\endgroup

A maintainer examines the state of Tcl Improvement Proposals (TIPs) and adds his comments to them at the TIP Document Collection.
\begin{itemize}
\item{}
\url{http://purl.org/tcl/home/cgi-bin/tct/tip/}
\end{itemize}

A maintainer may follow and participate in TCT discussions about TIPs and other matters concerning Tcl/Tk development on the TCLCORE mailing list.
\begin{itemize}
\item{}
\url{http://lists.sourceforge.net/lists/listinfo/tcl-core}
\end{itemize}

A maintainer may receive e-mail notification every time any change is made to any entry in Tcl's or Tk's Bug Tracker or Patch Manager by subscribing to the TCLBUGS mailing list.
\begin{itemize}
\item{}
\url{http://lists.sourceforge.net/lists/listinfo/tcl-bugs}
\end{itemize}

\section{There are multiple maintainers in my area. What do I do?}

The maintainer tasks are the same; you just have more hands to get the job done. It is up to the maintainers of an area to decide among themselves how they will divide the tasks. They might each take on a particular subset of files. Or they might let some maintainers fix bugs while others review new features. Or they might appoint one maintainer as the \textit{lead} and let him assign tasks to the others. Whatever works for you, and gets the work done.

\section{I found a bug in my area. What do I do?}

Bug finding and reporting is a job for the whole community, so when you find a bug, take off your maintainer hat. Report it to the Bug Tracker just like anyone would. If you recognize that the bug is in your area, go ahead and assign it to the Category for your area and to yourself or one of the other maintainers who share responsibility for that area.

\section{Why do I report the bug to myself?}

So that the bug appears in the database. Someone else may find it too, and when they go to report it to the Bug Tracker, they should discover that it's an already reported problem. A registered bug report is also the place where progress on fixing the bug can be recorded for all to see.

\section{There's a bug reported in the Category for the area I maintain. What do I do?}

First, understand the bug report. The best bug reports are clear and come with a demonstration script, but not all reports are so well crafted. You may need to exchange messages with the person who reported the bug. If the reporter logged in to SourceForge as \textit{username} before submitting a report, then you can write back to \textit{username@users.sourceforge.net}. If the bug was reported by \textit{nobody}, the best you can do is post a followup comment to the bug asking for more information, and hope the reporter comes back to check.

Next, confirm that the bug report is valid, original, and that it belongs in your area. Does it correctly assert that some public interface provided by your area behaves differently from its documented behavior? If not, then you should take the appropriate action:
\begin{enumerate}
\item{}
If the bug report notes a problem in another project, assign it to a Developer who is an Admin of the other project. Add a comment asking them to reassign to the correct project. Assigned To: \textit{an Admin of the other project}.

If no Developer is an Admin of the other project, or the other project isn't hosted by SourceForge, note the error in a comment, and mark the report invalid. Resolution: Invalid; Status: Closed; Assigned To: \textit{yourself}.
\item{}
If the bug report notes a problem due to a bug in another area, reassign it to the appropriate Category. Category: \textit{correct category}
\item{}
If the reporter's expectations are incorrect, point them to the documentation. You may also want to revise the documentation if it is not clear. Resolution: Invalid; Status: Closed; Assigned To: \textit{yourself}.
\item{}
If the bug report notes a problem already noted by another bug report, note the duplication. Resolution: Duplicate; Status: Closed; Assigned To: \textit{yourself}.
\item{}
If the bug report acknowledges that the code is behaving as documented, but argues that the documented behavior should be revised, then the report is a feature request rather than a bug report. More on handling feature requests below. Group: Feature Request.
\end{enumerate}

Valid, original bug reports in your area should be assigned to a maintainer of your area. If you are the only maintainer of your area, assign the bug to yourself. If there are multiple maintainers, you should decide among yourselves how to divide up the bug report assignments.

\section{There's a bug assigned to me. What do I do?}

Now we get the the heart of what a maintainer does. This is where you unleash the energies and talents you bring to the table. So, the best answer is ``Do what works best for you.'' The rest of this answer should be read as additional guidelines and tips that have worked well for others and might help you, but not as a mandatory checklist you must follow. If some advice below seems more burdensome than helpful, fall back to ``Do what works best for you.'' The goal is to register a patch that fixes the bug with the SourceForge Patch Manager. Do whatever helps you accomplish that goal.

Try to enlist the assistance of the person who reported the bug. This is especially important if the problem is platform-specific on a platform you do not have access to. Gaining the participation of the person who reported the bug can have many other benefits too. They see that progress is being made. They can offer additional insights they have, but left out of their original report. They can see how better bug reports lead to faster, better solutions, so their next reports may be of higher quality. They may even gain enough experience that their next report may come with the correction already attached. Eventually, they may even become maintainers themselves.

First, try to develop a test that demonstrates the bug and add it to the section of the test suite for your area. If the original bug report contained a demonstration script, perhaps you can adapt that. The new test will help you verify when you have fixed the bug.

If a fix for the bug is offered with the report, give it a try. Otherwise develop a fix yourself. Take care that while fixing the bug, you do not create new bugs by changing the correct behavior of other parts of the code in your section. The test suite for your area is very helpful. Use it.

It may become apparent that the best fix for your bug can only be accomplished after another bug is fixed first, or perhaps after a new feature is added to Tcl/Tk. In those cases, add a comment to the original bug report so those interested will know what is causing the delay. SourceForge may offer a way to denote these dependencies as well.

If you have trouble fixing the bug, ask for help. Try the other maintainers of your area first. Then try posting comments attached to the original bug report. Using \textit{cvs log}, you can get a list of developers who've recently made changes to the files you maintain. They might be able to offer advice, or explanations about why the code is the way it is. If none of these focused searches for help bears fruit, then try broader requests to the TCLCORE mailing lists, or the \url{news:comp.lang.tcl} newsgroup.

At any time, you may have several bugs assigned to you. It will help guide the expectations of the Tcl community if you can assign priority values to the bugs indicating the importance you assign to them. Try to work on fixing higher priority bugs before lower priority bugs. Some reasons you might give a bug a higher priority include:
\begin{enumerate}
\item{}
The bug causes a panic or core dump.
\item{}
Documentation is missing or incorrect.
\item{}
Other bug fixes are waiting on this bug fix.
\item{}
Several duplicate reports or ``me too'' comments about the bug are coming in from the community.
\end{enumerate}

Some reasons you might give a bug a lower priority include:
\begin{enumerate}
\item{}
A workaround is identified (add it as a comment attached to the bug report).
\item{}
Feature requests tend to get lower priority since they should be handled through the TIP process.
\end{enumerate}

Once you have crafted a fix for the bug, create a patch to the official source code (including the new tests that test for the fixed bug) and register it with the SourceForge Patch Manager. Note the number of the bug report fixed by the patch somewhere in the summary or comments associated with the patch. Assign the patch to yourself. Assign the Category to the area you maintain.

\section{There's a patch registered under the Category I maintain. What do I do?}

The SourceForge Patch Manager is used to review and revise patches before they are committed to the official source code. Your actions depend on what the patch does to your area, and who the patch is assigned to. The patch may change the public interface provided by your area (feature change); or the change may be completely internal (bug fix, or re-implementation) within your area. The patch may be assigned to you, to someone else, or to nobody. The person the patch is assigned to is the person who is leading the effort to integrate the patch into the official source code.

\section{What if the patch is assigned to nobody?}

The patch has probably been contributed by someone not on the list of Developers. It may be a contributed bug fix, or a contributed implementation of a TIP. Assign contributed bug fixes to the same maintainer who is assigned the corresponding bug report. If there is no corresponding bug report, add one. Assign TIP implementations to the Developer identified in the TIP as the one responsible for implementation of that TIP, or the TCT member who sponsored the TIP.

If the patch changes only your area (and shared or generated files), then leave the Category in your area. If the patch changes other areas as well as yours, change the category to None.

\section{What if the patch is assigned to me?}

Presumably you've assigned it to yourself to indicate that you're taking charge of integrating that patch into the official sources. If that's a mistake, treat the patch as if it were assigned to nobody. If you are the one leading the integration effort, see below (How do I integrate a patch into the official sources?).

\section{What if the patch is assigned to someone else?}

If the patch is assigned to another maintainer in your area, let him handle it. Leave it alone.

If the patch makes no changes in your area, change the Category of the patch to None.

If the patch makes changes in your area, and is assigned to a Developer who is not a maintainer of your area, that Developer is asking for review of the patch's changes to your area. You or one of the other maintainers of your area should review the patch and accept or reject it. Read on...

\section{What special review does a "feature change" patch require?}

Changes to the public interface of your section must be proposed to and accepted by the TCT through the TIP process before they can be added to the official Tcl source code. If the patch changes the public interface of your section, then there should be an associated TIP describing the new feature(s) that patch implements. Until there is such a TIP, and that TIP has been accepted by the TCT (check the value of the State header), you should not approve the patch.

Once there is an approved TIP corresponding to the patch, you should confirm that the patch correctly implements the accepted feature as described by the TIP. If not, you should not approve the patch.

After confirming that the patch correctly implements the feature change described in an accepted TIP, you should still review the technical merit of the patch's changes to your area before approving it.

\section{How do I review the technical merits of a patch?}

Apply the patch and run the test suites that cover your area. Check that the patch does not add any new test failures. If the patch is a bug fix, check that it actually fixes the bug. Think five times before approving a patch that causes new test failures or incompletely fixes a bug or incompletely implements an approved TIP.

Keep in mind that once the patch is integrated into the official sources, you'll be expected to maintain it. It is not in your interest to approve patches that make your job harder. Think four times before approving a patch that you do not understand.

Check that the patch keeps the features offered on different platforms consistent. If not, be certain that the documentation properly notes the platform-specific behavior. Think three times before approving a patch that causes the capabilities of Tcl/Tk to further diverge on different platforms.

Check that the patch follows Tcl's established coding conventions. See the Tcl/Tk Engineering Manual [\url{http://purl.org/tcl/home/doc/engManual.pdf}] and the Tcl Style Guide [\url{http://purl.org/tcl/home/doc/styleGuide.pdf}] for details. This is especially important when accepting contributed patches. Think twice before approving a patch that doesn't conform to these conventions.

Check the effect of the patch on the performance of Tcl/Tk. Use the tclbench set of benchmarks.
\begingroup\small\begin{verbatim}
cvs -d :pserver:anonymous@tcllib.cvs.sourceforge.net:/cvsroot/tcllib \
      checkout tclbench
\end{verbatim}
\endgroup

Think carefully before approving a patch that significantly degrades the performance of important operations.

Finally, while examining the patch, you may see a better way to accomplish the effect of the changes in your area. If you can provide that alternative implementation reasonably quickly, then propose it as a revision to the patch. However, be careful not to let the perfect be the enemy of the good. If a patch works, do not reject just because you can imagine a better way it could be done. Provide the better way, or accept the less good way in the patch, and leave migration to the better way for later when you have the time.

To approve the patch's changes to your area, simply note your approval in a followup comment on the patch. Indicate in your comment the Category of the area for which you approve the changes. If the patch changes multiple areas, set the Category of the patch back to None.

To reject the patch, you also indicate your rejection in a followup comment. You should explain the reasons for your rejection so that the patch can be revised with the goal of gaining your approval. If you can supply the needed revisions with reasonable effort, do so. If the patch changes multiple areas, set the Category of the patch back to None.

Unless the patch is assigned to you, do not change the Status of the patch. Leave that to the Developer assigned to the patch.

\section{How do I integrate a patch into the official sources?}

First you need the approval of at least one maintainer of each section changed by the patch.

\section{How do I get approval for integration?}

First, assign the patch to yourself to indicate that you are leading the integration effort. Next, determine the list of categories corresponding to the areas changed by the patch. It may help if you list them in a comment attached to the patch.

For each category in the list, assign the Category of the patch to that category. Then wait for a maintainer for that area to review the patch. If one approves it, then assign the next Category in the list. If maintainers for all areas on the list approve the same patch, you may integrate the patch into the official sources.

If a maintainer rejects the patch, revise the patch to address his concerns. Then start the review again. Start with the maintainer who rejected the first patch to be sure his concerns are addressed first.

Note that if the patch changes only the area you maintain, then you may immediately integrate the patch into the official sources once you are satisfied with it and it is registered in the Patch Manager.

\section{The patch is approved. How should it be integrated?}

Get a CVS working directory that is up to date with the HEAD branch of the official source repository. Apply the patch to your working directory, and then 'cvs commit' the changes to the HEAD branch.

At the same time you commit the patch, be sure to add an entry to the ChangeLog file describing the change. Follow the established format, which is derived from the GNU coding conventions. The description should be brief, but should describe the change reasonably completely. Include the SourceForge Bug and Patch ID numbers in the ChangeLog entry, but do not assume that the reader will have access to the Bug Tracker and Patch Manager to be able to understand the entry. You may assume the reader has access to the documentation.

Finally, with the patch integrated, change the Status of the patch in the Patch Manager to Accepted. If any bugs were fixed by the patch, change their Resolution to Fixed, and their Status to Closed.

\section{I want a patch review even though the patch changes only my area.}

Keep in mind that integrating a patch into the official sources is not an irreversible act. Commits to the HEAD branch will be checked out and tested by members of the Tcl community who are tracking Tcl/Tk development. Alpha and beta releases of Tcl/Tk that include your patch will also get your changes reviewed in practical settings.

That said, if you really want a pre-commit review of your patch, you can add a comment to the patch asking for review. Someone will probably respond. It's up to your judgment how long to wait, keeping in mind that you are the maintainer, so your judgment on the quality of patches in your area is implicitly trusted.

\section{What about CVS branches?}

When you integrate a patch into the official source code, you will usually 'cvs commit' the patch onto the HEAD branch. If the patch includes a feature change, it must (except in unusual circumstances approved by the TCT) be committed to the HEAD branch. The HEAD branch is the development branch from which alpha releases of Tcl/Tk are generated.

At any time, there is also one or more \textit{stable} branches of development. As of February, 2001, the branch 'core-8-3-1-branch' indicates the sequence of revisions from which the 8.3.x releases of Tcl/Tk are generated.

Since the Tcl Core Team took over development of Tcl/Tk, no changes have been committed to a stable branch, so we really have not established procedures on how we will decide what bug fixes should and should not be applied to the stable branch. It is possible that maintainers will be involved, though. It is also possible that a special team will be appointed to update the stable branch in preparation for the next stable release. In the case that you as a maintainer are asked to commit to the stable branch, be aware that the only patches that should be committed to a stable branch are those that fix bugs. No new features should be committed here.

The other kind of branch is a \textit{feature} branch. This is a development branch on which a sequence of several revisions may be committed as work in progress on a new feature, or re-implementation of existing features. Typically a feature branch will be created if the effort...
\begin{itemize}
\item{}
...touches on several functional areas;
\item{}
...is worked on jointly by several Developers;
\item{}
...is complex enough to require several revisions;
\item{}
...needs prototyping to determine the best TIP proposal to make; or
\item{}
...makes an incompatible change to Tcl/Tk that properly belongs on the next major version of Tcl/Tk before the HEAD branch has been designated for work toward the next major version.
\end{itemize}

As a Developer, feel free to create a feature branch if you have a reason to use one. Make a note of your branch tags in \cite{tip31}. Avoid the use of a branch tag matching core-* . Save the core-* branch tags for the tags of official stable branches and releases. To avoid conflict with other Developers, consider using your SourceForge login name as a prefix on the feature branch tags you create. Try to also make the branch tag descriptive of the purpose of the branch.

One big advantage of a feature branch is that any Developer may commit changes to a feature branch without all the publication, review, and approval overhead required when committing patches to the HEAD or stable branches. On the feature branches you can go through multiple revisions reasonably quickly and spend the administrative overhead only at the end when it is time to apply the finished product to the official branches.

\section{What other things does a maintainer do?}

The tasks of fixing bugs and approving and committing patches to the official source code of Tcl and Tk are the core tasks that maintainers perform. That's all the job actually requires.

You will probably want to keep an eye on the TCT's plans for Tcl/Tk development as well. If a TIP proposes a new feature in your area, it is in your interest to know about it, and propose revisions and improvements to it. Ultimately you will be asked to approve the patch that implements the new feature, and then you will be expected to maintain it, so if you have concerns about a proposal, it's best to make them known early. TCT members will probably ask your opinion on TIPs that propose changes to your area for this reason.

\section{Comments}

Please add your comments here.

\begin{quote}
Well, since I drafted this SourceForge has replaced the Bug Tracker and Patch Manager with a \textit{Tracker}. This TIP \textit{really} needs revision now.
\end{quote}

\section{Copyright}

This document has been placed in the public domain.

\section{Colophon}

\textit{TIP AutoGenerator --- written by Donal K. Fellows}
\begin{thebibliography}{TIP \#4}
\addcontentsline{toc}{section}{References}
\bibitem[TIP \#0]{tip0}
John Ousterhout,
\emph{Tcl Core Team Basic Rules},
on-line at \url{http://purl.org/tcl/tip/0.html}
\bibitem[TIP \#16]{tip16}
Don Porter,
Daniel Steffen,
\emph{Tcl Functional Areas for Maintainer Assignments},
on-line at \url{http://purl.org/tcl/tip/16.html}
\bibitem[TIP \#23]{tip23}
Kevin Kenny,
Jim Ingham,
Don Porter,
Daniel A. Steffen,
Donal K. Fellows,
\emph{Tk Toolkit Functional Areas for Maintainer Assignments},
on-line at \url{http://purl.org/tcl/tip/23.html}
\bibitem[TIP \#31]{tip31}
Don Porter,
miguel sofer,
Jeff Hobbs,
Kevin Kenny,
David Gravereaux,
Donal K. Fellows,
Andreas Kupries,
Donal K. Fellows,
,
Kevin Kenny,
\emph{CVS tags in the Tcl and Tk repositories},
on-line at \url{http://purl.org/tcl/tip/31.html}
\end{thebibliography}
\end{document}
