Add parens after options, and shorten the argument labels to match glm.sthlp

Parens already existed after options in the summary table,
but not in the paragraph documentation.

{smcl}
{* *! version 0.0.1  28may2015}{...}
{vieweralsosee "[R] regress" "mansection R regress"}{...}
{viewerjumpto "Syntax" "svm##syntax"}{...}
{viewerjumpto "Description" "svm##description"}{...}
{viewerjumpto "Options" "svm##options"}{...}
{viewerjumpto "Examples" "svm##examples"}{...}
{viewerjumpto "Stored results" "svm##results"}{...}
{viewerjumpto "Gotchas" "svm##gotchas"}{...}
{viewerjumpto "Copyright" "svm##copyright"}{...}
{viewerjumpto "Authors" "svm##authors"}{...}
{viewerjumpto "References" "svm##references"}{...}
{...}{* NB: these hide the newlines }
{...}
{...}
{title:Title}

{p2colset 5 18 20 2}{...}
{p2col :{cmd:svm} {hline 2}}Support Vector Machines{p_end}
{p2colreset}{...}

{marker syntax}{...}
{title:Syntax}

{p 8 16 2}
{help svm##svm:svm} {depvar} {indepvars} {ifin} [{cmd:,} {it:options}]

{p 8 16 2}
{help svm##svm:svm} {indepvars} {ifin}, type({help svm##one_class:one_class}) [{it:options}]

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{syntab:Model}
{synopt :{opth t:ype(svm##type:type)}}Type of model to fit: {opt c_svc}, {opt nu_svc}, {opt one_class}, {opt epsilon_svr}, or {opt nu_svr}. Default: {cmd:type(c_svc)}{p_end}
{synopt :{opth k:ernel(svm##kernel:kernel)}}SVM kernel function to use: {opt linear}, {opt poly}, {opt rbf}, {opt sigmoid}, or {opt precomputed}. Default: {cmd:kernel(rbf)}{p_end}

{synopt :{opth deg:ree(svm##degree:#)}}For {opt poly} kernels, the degree of the polynomial to use. Default: cubic ({cmd:degree(3)}){p_end}

{* XXX the division between 'tuning' and 'model' parameters is hazy; e.g. you could in theory cross-validate to choose degree (and people do this with neural networks), or even to choose the kernel . hmmmmm}{...}

{syntab:Tuning}
{synopt :{opth c:(svm##c:#)}}For {opt c_svc}, {opt epsilon_svr} and {opt nu_svr} SVMs, this is a regularization parameter which weights the slack variables. Should be > 0. Default: {cmd:c(1)}{p_end}
{synopt :{opth eps:ilon(svm##epsilon:#)}}For {opt epsilon_svr} SVMs, the margin of error allowed for to count something as a support vector. Default: {cmd:p(0.1)}{p_end}
{synopt :{opth nu:(svm##nu:#)}}For {opt nu_svc}, {opt one_class}, and {opt nu_svr} SVMs, tunes the proportion of expected support vectors. Should be in (0, 1]. Default: {cmd:nu(0.5)}{p_end}

{synopt :{opth gamma:(svm##gamma:#)}}For {opt poly}, {opt rbf} and {opt sigmoid} kernels, a scaling factor for the linear part of the kernel. Default: {cmd:gamma(1/[# {indepvars}])}{p_end}
{synopt :{opth coef0:(svm##coef0:#)}}For {opt poly} and {opt sigmoid} kernels, a bias ("intercept") term for the linear part of the kernel. Default: {cmd:coef0(0)}{p_end}

{synopt :{opt shrink:ing}}Whether to use {help svm##shrinking:shrinkage} heuristics to improve the fit. Default: disabled{p_end}

{syntab:Features}
{* {synopt :{opt norm:alize}}Whether to {help svm##normalize:center and scale} the data. NOT IMPLEMENTED. Default: disabled{p_end} }
{synopt :{opt prob:ability}}Whether to {help svm##probability:prepare} for "predict, prob" during estimation. Only applicable to classification problems. Default: disabled{p_end}
{synopt :{opth sv:(svm##sv:newvarname)}}If given, an indicator variable to generate to mark each row as a support vector or not. Default: disabled{p_end}

{syntab:Performance}
{synopt :{opth tol:erance(svm##tolerance:#)}}The stopping tolerance used to decide when convergence has happened. Default: {cmd:epsilon(0.001)}{p_end}
{synopt :{opt v:erbose}}Turns on {help svm##verbose:verbose mode}. NOT IMPLEMENTED. Default: disabled{p_end}
{synopt :{opth cache:_size(svm##cache_size:#)}}The size of the RAM cache used during fitting, in megabytes. Default: 100MB ({cmd:cache_size(100)}){p_end}

{synoptline}
{pstd}All variables must be numeric, including categorical variables,
which may appear to be strings if they have have {help label values:value labels} attached, but are probably actually stored as {help byte}s.
If you in fact have categories stored in string variables use {help encode}.{p_end}
INCLUDE help fvvarlist

{p 8 16 2}
{help svm##predict:predict} {newvar} {ifin}, [{cmdab:prob:ability}]

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{synopt :{opt prob:ability}}If specified, estimate class probabilities for each observation. This only makes sense for classification problems.{p_end}

{p 8 16 2}
{help svm##export:svm_export} using {filename}

{p 8 16 2}
{help svm##export:svm_import} using {filename}

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{synopt :{opt using}}The filename to export to or import from a fitted libsvm model.{p_end}

{marker description}{...}
{title:Description}

{pstd}
{cmd:svm} fits a support vector machine (SVM) model.
SVM is not one, but several, variant models each based upon the principles of
splitting hyperplanes and the culling of unimportant observations.

{pstd}
The basic SVM idea is to find a linear boundary---a hyperplane---in high-dimensional space:
for classification, this is a boundary between two classes;
for regression it is a line {help svm##epsilon:near} which points should be--much like in {help regess:OLS},
while simultaneously minimizing the number of observations required to distinguish
this hyperplane.
The unimportant observations are ignored after fitting is done, which makes SVM very memory efficient.

{pstd}
Each observation can be thought of as a vector, in the linear algebra sense,
so the {it:support vectors} are those observations which the algorithm deems critical to the fit.

{pstd}

{pstd}
This package is a thin wrapper for the widely deployed {help svm##libsvm:libsvm},
so in addition to the Stata package, {bf:libsvm must be installed}.
On Windows, libsvm.dll is bundled with the package,
and you can find it in your {help adopath} (try {cmd:findfile libsvm.dll} to verify this).
On OS X, libsvm is available in both {browse "https://brew.sh":brew} and {browse "https://www.macports.org":macports}.
On Linux, search for libsvm in your distribution's package manager.
You can also compile and install libsvm from source,
if you cannot find it in your package manager or if you want the latest libsvm.
If you are having plugin load errors, please {help svm##authors:contact the authors},
as we want to make the experience as smooth as possible for our users across as many platforms as possible.

{pstd}
The thinness of this wrapper is an intentional feature. It means work done under
Stata-SVM should be replicable with other libsvm wrappers such as
{browse "http://weka.wikispaces.com/LibSVM":Weka} or
{browse "http://scikit-learn.org/stable/modules/svm.html":sklearn}.
We will {help svm##authors:listen to} feature requests,
but mostly we will try to get features rolled into libsvm
so that everyone can benefit unless the feature really is Stata-specific.

{pstd}
See {help svm##svmtutorial:this SVM tutorial} for a gentle introduction to the method.
IF this manual is confusing, refer to the authoritative
the libsvm {browse "http://www.csie.ntu.edu.tw/~cjlin/libsvm/faq.html":FAQ},
{browse "https://github.com/cjlin1/libsvm/blob/master/README":README},
and {help svm##libsvm:implementation paper}.
Then please write us with suggestions for clarification.

{marker options}{...}
{title:Options}

{marker svm}{...}
{dlgtab:svm}{* this is a misuse of dlgtab because I have no corresponding dialog, but it drastically helps readability }

{pstd}
{cmd:svm} fits an SVM model, fitting {depvar} to {indepvars} except under {opt type(one_class)} which only uses {indepvars}.

{pstd}
libsvm has several algorithms with a single entry point. Since this is a thin wrapper, so do we.
Hence, awkwardly, not all combinations of options are valid.
Usually, libsvm will give an error if you specify an invalid combination,
but sometimes it just ignores the extra parameters {bf:without telling you}.
Further, amongst valid combinations, not all options and datasets give good results.
Our goal is to have sane defaults,
so that the only choice you usually need to make is what {help svm##type:type} and {help svm##kernel:kernel} to use,
but there is no way to give reasonable defaults for all datasets.
The {help svm##libsvmguide:libsvm guide} has in-depth coverage of issues in parameter specification.

{* MODEL PARAMS: }
{phang}
{marker type}{...}
{opt type(type)} specifies which SVM model to run.{p_end}
{pmore}{opt c_svc} and {opt nu_svc} perform classification.{p_end}
{pmore2}{depvar} should be a variable containing categories.
If you try to use floating point values with classification you
will find that they are truncated mercilessly to their integer parts,
so you may need to recode your categories first.
If you end up with almost as many classes as observations,
you have probably used a continuous {depvar} and
should use regression instead.{p_end}

{pmore2}Multiclass classification is automatically handled using the {browse "http://en.wikipedia.org/wiki/Multiclass_classification":class-against-class} method.
You do not need to do anything special to invoke it.{p_end}

{pmore}{opt epsilon_svr} and {opt nu_svr} perform regression.{p_end}
{pmore2}{depvar} should be a variable containing continuous values.{p_end}
{pmore2}While you can use this with discrete {depvar}s, it is more common to use it with continuous ones.{p_end}
{pmore2}See {help svm##svr_tutorial:the SVR tutorial} for more details.{p_end}

{marker one_class}{...}
{pmore}{opt one_class} separates outliers from the bulk of the data.{p_end}
{pmore2}{opt one_class} does not take a {depvar} because it is a form of unsupervised learning: all the information it uses is in the predictors themselves.{p_end}
{pmore2}Its predictions give 1 for "data" and -1 for "outlier".{p_end}
{pmore3}{bf:Tip:} You may use the same {varlist} as with the other types:
 this just interprets your {depvar} as one of the {indepvars},
 giving {opt one_class} more information to work with.{p_end}

{pmore}To learn about the {opt nu} variants, see {help svm##nusvm:Chen and Lin's ν-SVM tutorial}.{p_end}

{pmore}{it:type} is case insensitive.{p_end}

{phang}
{marker kernel}{...}
{opt kernel(kernel)} gives a kernel function to use.
The basic SVM algorithm is based on linear, euclidean, space. Much like {help glm:GLMs},
 the {browse "https://en.wikipedia.org/wiki/Kernel_Method":kernel trick}
 extends the algorithm to nonlinear data.{p_end}
{pmore}
Kernels in essense bend a non-linear space into a linear one by applying a high-dimensional mapping.
Under high enough dimensions, any set of data looks close to linear.
See {browse "https://www.youtube.com/watch?v=3liCbRZPrZA"} for a visualization of this process.
{p_end}
{pmore}
The trick part---and the reason why the set of kernels is hardcoded---is that
for certain kernels [CITATION NEEDED] the fit can be done efficiently without
actually constructing the high-dimensional points, as the estimation only cares
scoring coefficients u using the output of the kernel, not the output of the values the kernel
is, in theory, operating upon.{p_end}

{pmore}Kernels available in this implementation are:{p_end}
{pmore2}{opt linear} means the linear kernel you are probably familiar with from {help regress:OLS}: u'*v{p_end}
{pmore2}{opt poly} is (gamma*u'*v + coef0)^degree{p_end}
{pmore2}{opt rbf} stands for Radial Basis Functions, and treats the coefficients as a mean to smoothly approach in a ball, with the form exp(-gamma*|u-v|^2); this kernel tends to be good at [TODO].{p_end}
{pmore2}{opt sigmoid} is a kernel which bends the linear kernel to fit in -1 to 1 with tanh(gamma*u'*v + coef0). {help logistic} regression uses the similar logistic function for a sigmoidal non-linearity.{p_end}
{pmore2}{opt precomputed} assumes that {depvar} is actually a list of precomputed kernel values. With effort, you can use this to use custom kernels with your data [TODO].{p_end}

{pmore}{it:kernel} is case insensitive.{p_end}

{phang}
{marker degree}{...}
{opt degree(#)} selects the degree of the polynomial used by the {opt poly} kernel.
Setting this too high will result in overfitting. Setting it too low may result in non-convergence.
[TODO: tips about choosing this]

{* TUNING PARAMS: }
{phang}
{marker c}{...}
{opt c(#)} weights (regularizes) the slack variables used in {opt c_svc}, {opt epsilon_svr} and {opt nu_svr}.
If your fit is poor, try cranking this up.
{* XXX NOT TRUE: This will pre-multiply any sample-specific {opt weight}s. }

{phang}
{marker epsilon}{...}
{opt epsilon(#)} is the margin of error allowed by {opt epsilon_svr}.
Larger makes your fit more able to incorporate more observations, but can lead to underfitting.
Smaller can lead to overfitting.

{phang}
{marker nu}{...}
{opt nu(#)} is used in the nu variants.
The nu variants are a reparamaterization of regular SVM which lets you directly tune,
using {opt nu}, the tradeoff between error and computation.
{opt nu} is an upper bound on the fraction of training errors and a lower bound of the
fraction of support vectors, so that [TODO smaller means ... and larger means .....]
See {help svm##nusvm:the ν-SVM tutorial} for details.
{* ..wait... this doesn't make any sense. nu == 0.1 means there are at most 10% (training) errors and at least 10% are support vectors.}{...}
{*      nu = 0.9 means there are at most 90% errors and at least 90% SVs.   you should always choose 0, then, to get perfect prediction and zero memory usage}{...}

{phang}
{marker gamma}{...}
{opt gamma(#)} is used in the non-linear kernels as a scaling factor for the linear part, as seen above.
[TODO: tips about choosing this]

{phang}
{marker coef0}{...}
{opt coef0(#)} similarly is used in the non-linear kernels as a pseudo-intercept term.
Except it is not used in the {opt rbf} kernel as {opt rbf} is essentially a distance function, and biasing would be pointless.[???]
[TODO: tips about choosing this]

{phang}
{marker shrinking}{...}
{opt shrinking} invokes the shrinkage heuristics [TODO: what does this mean? shrinkage estimation?],
which reduce can improve the fit by trading bias for variance: by giving up accuracy in the estimates,
variance of the estimates can be reduced, which is sometimes better overall.

{* FEATURE PARAMS: }{...}
{* {marker normalize}{...} }
{* {phang} }{...}
{* {opt normalize} instructs the estimation to first center and scale the data }{...}
{* as SVM tends to be very sensitive to scaling issues. }{...}
{* This normalizes all data to [0,1] using min-max normalization, as suggested in the {help svm##libsvmguide:libsvm guide}. }{...}
{* Normalization creates temporary variables, so you may prefer to preprocess the data yourself---destructively and in-place---to save time on re-estimations and memory for variables, }{...}
{* especially if you are bumping up against your Stata system limits. You may find {cmd:ssc install center} helpful }{...}

{phang}
{marker probability}{...}
{opt probability} enables the use of "{help svm##predict_prob:predict, prob}".
That does {browse "https://en.wikipedia.org/wiki/Platt_scaling":Platt scaling},
so for each class-against-class this precomputes a logistic regression
which is tuned with 5-fold cross-validation,
hence enabling this takes a great deal of additional CPU and RAM.

{phang}
{marker sv}{...}
{opt sv(newvarname)} records in the given variable a boolean indicating whether each observation was determined to be a support vector.

{* PERFORMANCE PARAMS: }
{phang}
{marker tolerance}{...}
{opt tolerance(#)} is the stopping tolerance used by the numerical optimizer. You could widen this if you are finding convergence is slow,
 but be aware that this usually non-convergence is a deeper problem in the interaction of data, kernel, and tuning parameters.

{phang}
{marker verbose}{...}
{opt verbose} enables output from the low level libsvm code for the duration of the operation.

{phang}
{marker cache_size}{...}
{opt cache_size(#)} controls a time-memory tradeoff during estimation.
Value is how many megabytes (MB) of RAM to set aside for caching data points as the optimizer is iterated.
Generally, more is faster, at least until you run out of RAM or cause your machine to start swapping.

{...}
{...}

{marker predict}{...}
{dlgtab:predict}

{pstd}After training you can ask svm to {cmd:predict} what the category (classification) or outcome value (regression)
      should be for each given observation. Results are placed into {newvar}.{p_end}
{pstd}{newvar} must not exist, so if you want to repredict your choices are {cmd:drop {newvar}} or to pick a new name, e.g. {cmd:predict {newvar}2}.{p_end}

{marker predict_prob}{...}
{phang}For classification ({opt c_svc}, {opt nu_svc}) problems, {opt probability} requests, for each observation, the probability of it being each class.
{newvar} is used as a stem for the new columns.
This option is not valid for other SVM types.{p_end}

{phang}
{opt verbose}: see {help svm##verbose:svm, verbose}.

{pstd}
Prediction implicitly uses the same {indepvars} as during estimation, so be careful about renaming or dropping variables.
{...}
{...}

{marker export}{...}
{dlgtab:import/export}

{pstd}
libsvm has an ad-hoc format it uses to save trained models.
The command line programs {cmd:svm-train} and {cmd:svm-predict} which come with libsvm communicate via it.
We support this format for interoperability.

{pstd}
{cmd:svm_export} will write a fitted model to disk.
This can only be run if there is one in memory, either from {help svm##svm:svm} or {help svm##export:svm_import}.
Conventionally you should name your file with '.model' for a file extension.

{pstd}
{cmd:svm_import} will load a model from disk, replacing any previous in-memory fit.
When you import, [TODO: some properties] will be missing because the import was done without reference to any dataset.

{phang}
{opt verbose}: see {help svm##verbose:svm, verbose}.

{pstd}
Do not confuse these commands with {help svmlight:import_svmlight and export_svmlight}.

{marker examples}{...}
{title:Examples:  classification}

        {hline}
{phang2}{cmd:. webuse highschool, clear}{p_end}
{phang2}{cmd:. svm gender}{p_end}
{phang2}{cmd:. ereturn list}{p_end}
{phang2}{cmd:. predict P, prob}{p_end}
        {hline}

{title:Examples:  regression, suppressing sv_coef}

        {hline}
{phang2}{cmd:. sysuse auto, clear}{p_end}
        {hline}

{title:Examples:  classification with tuning}

        {hline}
{phang2}{cmd:. webuse regsmpl, clear}{p_end}
        {hline}

{title:Examples:  viewing support vectors}

--- crosstab sv against category and compare to nSV

{title:Examples:  forecasting}

        {hline}
--- train on one set of data, generate more observations, predict
        {hline}

{title:Examples:  custom kernel}

        {hline}
--- use hacks + kernel(precomputed)
        {hline}

{marker results}{...}
{title:Stored results}

{pstd}
{cmd:svm} and {cmd:svm_import} stores the following in {cmd:e()}:

{synoptset 20 tabbed}{...}
{* Note: svm_model components left unexposed: }{...}
{*      - SV, the actual SVs found. If you want this in a new fit, you can use the sv() option; the use case of inspecting old SVs on an imported fit is rare, we assume, and we do not support it; (they will still be loaded to memory }{...}
{*      - sv_indices, the indexes of which support vectors were found; exposed indirectly with the sv() option }{...}
{*      - probA, probB, the coefficients used for predict, prob; these are not, by themselves, interesting }{...}
{*      - label, the "labels" of the classes (which are the integers libsvm casts out of the initial dataset; exported with strLabels to be used for labelling rho and sv_coef, but otherwise not directly interesting and  }{...}
{*      - nSV, the number of SVs per class; this is only interesting for classifications, and it duplicates what you can get out of "tab `e(depvar)' SV" }{...}
{*      - free_sv, internal libsvm flag which is a hack to stretch svm_model to handle creation from both svm_train() and svm_import() }{...}
{p2col 5 20 24 2: Scalars}{p_end}
{synopt:{cmd:e(N)}}number of observations{p_end}
{synopt:{cmd:e(N_class)}}number of classes, in a classification problem. {opt 2} in a regression problem.{p_end}
{synopt:{cmd:e(N_SV)}}number of support vectors.
If {opt e(N_SV)}/{opt e(N)} is close to 100% your fit is inefficient; perhaps you need to adjust your {help svm##kernel:kernel}.
{p_end}

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Macros}{p_end}
{synopt:{cmd:e(cmd)}}"{cmd:svm}"{p_end}
{synopt:{cmd:e(cmdline)}}command as typed{p_end}
{synopt:{cmd:e(depvar)}}name of dependent variable{p_end}
{synopt:{cmd:e(title)}}title in estimation output{p_end}
{synopt:{cmd:e(model)}}"{cmd:svm}"{p_end}
{synopt:{cmd:e(svm_type)}}SVM type string, as above{p_end}
{synopt:{cmd:e(svm_kernel)}}kernel string, as above{p_end}
{synopt:{cmd:e(predict)}}program used to implement {cmd:predict}{p_end}
{synopt:{cmd:e(levels)}}list of the classes detected, in the order they were detected, if applicable{p_end}
{* {synopt:{cmd:e(estat_cmd)}}program used to implement {cmd:estat}{p_end} }{...}

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Matrices}({help svm##gotchas:may be missing}){p_end}
{synopt:{cmd:e(sv_coef)}}The coefficients of the support vectors for each fitted hyperplane. [TODO]{p_end}
{synopt:{cmd:e(rho)}}The intercept term for each fitted hyperplane. It is lower-triangular and {cmd:e(N_class)}^2 large, with each entry [i,j] representing the hyperplane between class i and class j.{p_end}

{* XXX this section title should be more formal}{...}
{marker gotchas}{...}
{title:Gotchas}

{pstd}
{bf:Memory Limits}: The cheaper versions of Stata allow only allow less variables and smaller matrices to be used.
As machine learning problems typically are on very large datasets,
it is easy to inadvertently instruct this package to construct more columns or larger matrices than you can afford.
If you overflow {help maxvar}, you will receive an error, the operation will fail, and the dataset will be left untouched.
If you overflow {help matsize}, the matrix that overflowed will be missing, but operation will otherwise succeed.

{pmore}
If Stata's memory limits are an impossible hurdle,
your best option is to give up on Stata and switching to libsvm's companion {cmd:svm-train} program.
This will have been installed with the libsvm package if you used a package manager, or
you can get it {browse "http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/libsvm.cgi?+http://www.csie.ntu.edu.tw/~cjlin/libsvm+zip":from its authors};
You can use {help svmlight:export svmlight} to extract your dataset for use with {cmd:svm-train}.

{marker copyright}{...}
{title:Copyright}

{pstd}
The wrapper is licensed:

{pmore}
The MIT License (MIT)

{pmore}
Except where otherwise noted in the code, Copyright (c) 2015
Nick Guenther <nguenthe@uwaterloo.ca>, Matthias Schonlau <schonlau@uwaterloo.ca>

{pmore}
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

{pmore}
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

{pmore}
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

{pstd}
libsvm is licensed:

{pmore}
Copyright (c) 2000-2014 Chih-Chung Chang and Chih-Jen Lin
All rights reserved.

{pmore}
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

{pmore}
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

{pmore}
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

{pmore}
3. Neither name of copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

{pmore}
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

{marker authors}{...}
{title:Authors}

{pstd}
Though the license does not obligate you in any way to do so, if you find
this software useful we would be curious and appreciative to hear about your
adventures in machine learning with Stata.{p_end}
{pmore}Thank you.

{pstd}You can contact us at{p_end}
{pmore}* Nick Guenther <nguenthe@uwaterloo.ca>{p_end}
{pmore}* Matthias Schonlau <schonlau@uwaterloo.ca>{p_end}

{marker references}{...}
{title:References}

{marker sourcecode}{...}
{phang}
Guenther, Nick and Schonlau, Matthias. 2015.
{it:Stata-SVM}.
{browse "https://github.com/kousu/statasvm/"}.
{p_end}

{marker svmtutorial}{...}
{phang}
Bennett, Kristin P., and Colin Campbell. 2000.
{it:Support Vector Machines: Hype or Hallelujah?}
SIGKDD Explor. Newsl. 2.2: 1–13.
{browse "http://www.svms.org/tutorials/BennettCampbell2000.pdf"}.
{p_end}

{marker libsvm}{...}
{phang}
Chang, Chih-Chung and Lin, Chih-Jen. 2011.
{it:LIBSVM: a library for support vector machines.}
ACM Transactions on Intelligent Systems and Technology, 2:27:1--27:27.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/libsvm.pdf"}.
Software available at {browse "http://www.csie.ntu.edu.tw/~cjlin/libsvm"}
{p_end}

{marker libsvmguide}{...}
{phang}
Hsu, Chih-Wei, Chang, Chih-Chung, and Lin, Chih-Jen. April 15, 2010.
{it:A Practical Guide to Support Vector Classification}.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf"}.
{p_end}

{marker svr_tutorial}{...}
{phang}
Smola, Alex J., and Schölkopf, Bernhard. 2004.
{it:A tutorial on support vector regression}.
Statistics and Computing 14.3: 199–222.
{* This one is behind a paywall, so the best we can do is a give a DOI link }{...}
{browse "http://dx.doi.org/10.1023/b:stco.0000035301.49549.88"}.
{p_end}

{marker nusvm}{...}
{phang}
Chen, Pai-Hsuen, Lin Chih-Jen, and Schölkopf, Bernhard. 2005.
{it:A Tutorial on ν-Support Vector Machines}.
Applied Stochastic Models in Business and Industry 21.2: 111–136.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/nusvmtutorial.pdf"}.
{p_end}

{smcl}
{* *! version 0.0.1  28may2015}{...}
{vieweralsosee "[R] regress" "mansection R regress"}{...}
{viewerjumpto "Syntax" "svm##syntax"}{...}
{viewerjumpto "Description" "svm##description"}{...}
{viewerjumpto "Options" "svm##options"}{...}
{viewerjumpto "Examples" "svm##examples"}{...}
{viewerjumpto "Stored results" "svm##results"}{...}
{viewerjumpto "Gotchas" "svm##gotchas"}{...}
{viewerjumpto "Copyright" "svm##copyright"}{...}
{viewerjumpto "Authors" "svm##authors"}{...}
{viewerjumpto "References" "svm##references"}{...}
{...}{* NB: these hide the newlines }
{...}
{...}
{title:Title}

{p2colset 5 18 20 2}{...}
{p2col :{cmd:svm} {hline 2}}Support Vector Machines{p_end}
{p2colreset}{...}

{marker syntax}{...}
{title:Syntax}

{p 8 16 2}
{help svm##svm:svm} {depvar} {indepvars} {ifin} [{cmd:,} {it:options}]

{p 8 16 2}
{help svm##svm:svm} {indepvars} {ifin}, type({help svm##one_class:one_class}) [{it:options}]

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{syntab:Model}
{synopt :{opth t:ype(svm##type:type)}}Type of model to fit: {opt c_svc}, {opt nu_svc}, {opt one_class}, {opt epsilon_svr}, or {opt nu_svr}. Default: {cmd:type(c_svc)}{p_end}
{synopt :{opth k:ernel(svm##kernel:kernel)}}SVM kernel function to use: {opt linear}, {opt poly}, {opt rbf}, {opt sigmoid}, or {opt precomputed}. Default: {cmd:kernel(rbf)}{p_end}

{synopt :{opth deg:ree(svm##degree:degree)}}For {opt poly} kernels, the degree of the polynomial to use. Default: cubic ({cmd:degree(3)}){p_end}

{* XXX the division between 'tuning' and 'model' parameters is hazy; e.g. you could in theory cross-validate to choose degree (and people do this with neural networks), or even to choose the kernel . hmmmmm}{...}

{syntab:Tuning}
{synopt :{opth c:(svm##c:c)}}For {opt c_svc}, {opt epsilon_svr} and {opt nu_svr} SVMs, this is a regularization parameter which weights the slack variables. Should be > 0. Default: {cmd:c(1)}{p_end}
{synopt :{opth eps:ilon(svm##epsilon:eps)}}For {opt epsilon_svr} SVMs, the margin of error allowed for to count something as a support vector. Default: {cmd:p(0.1)}{p_end}
{synopt :{opth nu:(svm##nu:nu)}}For {opt nu_svc}, {opt one_class}, and {opt nu_svr} SVMs, tunes the number of supports vectors. Should be in (0, 1]. Default: {cmd:nu(0.5)}{p_end}

{synopt :{opth gamma:(svm##gamma:gamma)}}For {opt poly}, {opt rbf} and {opt sigmoid} kernels, a scaling factor for the linear part of the kernel. Default: {cmd:gamma(1/[# {indepvars}])}{p_end}
{synopt :{opth coef0:(svm##coef0:coef0)}}For {opt poly} and {opt sigmoid} kernels, a bias ("intercept") term for the linear part of the kernel. Default: {cmd:coef0(0)}{p_end}

{synopt :{opt shrink:ing}}Whether to use {help svm##shrinking:shrinkage} heuristics to improve the fit. Default: disabled{p_end}

{syntab:Features}
{* {synopt :{opt norm:alize}}Whether to {help svm##normalize:center and scale} the data. NOT IMPLEMENTED. Default: disabled{p_end} }
{synopt :{opt prob:ability}}Whether to {help svm##probability:prepare} for "predict, prob" during estimation. Only applicable to classification problems. Default: disabled{p_end}
{synopt :{opth sv:(svm##sv:newvarname)}}If given, an indicator variable to generate to mark each row as a support vector or not. Default: disabled{p_end}

{syntab:Performance}
{synopt :{opth tol:erance(svm##tolerance:tol)}}The stopping tolerance used to decide when convergence has happened. Default: {cmd:epsilon(0.001)}{p_end}
{synopt :{opt v:erbose}}Turns on {help svm##verbose:verbose mode}. NOT IMPLEMENTED. Default: disabled{p_end}
{synopt :{opth cache:_size(svm##cache_size:cache)}}The size of the RAM cache used during fitting, in megabytes. Default: 100MB ({cmd:cache_size(100)}){p_end}

{synoptline}
{pstd}All variables must be numeric, including categorical variables,
which may appear to be strings if they have have {help label values:value labels} attached, but are probably actually stored as {help byte}s.
If you in fact have categories stored in string variables use {help encode}.{p_end}
INCLUDE help fvvarlist

{p 8 16 2}
{help svm##predict:predict} {newvar} {ifin}, [{cmdab:prob:ability}]

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{synopt :{cmdab:prob:ability}}If specified, estimate class probabilities for each observation. This only makes sense for classification problems.{p_end}

{p 8 16 2}
{help svm##export:svm_export} using {filename}

{p 8 16 2}
{help svm##export:svm_import} using {filename}

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{synopt :{cmd: using}}The filename to export to or import from a fitted libsvm model.{p_end}

{marker description}{...}
{title:Description}

{pstd}
{cmd:svm} fits a support vector machine (SVM) model.
SVM is not one, but several, variant models each based upon the principles of
splitting hyperplanes and the culling of unimportant observations.

{pstd}
The basic SVM idea is to find a linear boundary---a hyperplane---in high-dimensional space:
for classification, this is a boundary between two classes;
for regression it is a line {help svm##epsilon:near} which points should be--much like in {help regess:OLS},
while simultaneously minimizing the number of observations required to distinguish
this hyperplane.
The unimportant observations are ignored after fitting is done, which makes SVM very memory efficient.

{pstd}
Each observation can be thought of as a vector, in the linear algebra sense,
so the {it:support vectors} are those observations which the algorithm deems critical to the fit.

{pstd}

{pstd}
This package is a thin wrapper for the widely deployed {help svm##libsvm:libsvm},
so in addition to the Stata package, {bf:libsvm must be installed}.
On Windows, libsvm.dll is bundled with the package,
and you can find it in your {help adopath} (try {cmd:findfile libsvm.dll} to verify this).
On OS X, libsvm is available in both {browse "https://brew.sh":brew} and {browse "https://www.macports.org":macports}.
On Linux, search for libsvm in your distribution's package manager.
You can also compile and install libsvm from source,
if you cannot find it in your package manager or if you want the latest libsvm.
If you are having plugin load errors, please {help svm##authors:contact the authors},
as we want to make the experience as smooth as possible for our users across as many platforms as possible.

{pstd}
The thinness of this wrapper is an intentional feature. It means work done under
Stata-SVM should be replicable with other libsvm wrappers such as
{browse "http://weka.wikispaces.com/LibSVM":Weka} or
{browse "http://scikit-learn.org/stable/modules/svm.html":sklearn}.
We will {help svm##authors:listen to} feature requests,
but mostly we will try to get features rolled into libsvm
so that everyone can benefit unless the feature really is Stata-specific.

{pstd}
See {help svm##svmtutorial:this SVM tutorial} for a gentle introduction to the method.
IF this manual is confusing, refer to the authoritative
the libsvm {browse "http://www.csie.ntu.edu.tw/~cjlin/libsvm/faq.html":FAQ},
{browse "https://github.com/cjlin1/libsvm/blob/master/README":README},
and {help svm##libsvm:implementation paper}.
Then please write us with suggestions for clarification.

{marker options}{...}
{title:Options}

{marker svm}{...}
{dlgtab:svm}{* this is a misuse of dlgtab because I have no corresponding dialog, but it drastically helps readability }

{pstd}
{cmd:svm} fits an SVM model, fitting {depvar} to {indepvars} except under {opt type(one_class)} which only uses {indepvars}.

{pstd}
libsvm has several algorithms with a single entry point. Since this is a thin wrapper, so do we.
Hence, awkwardly, not all combinations of options are valid.
You will get an error if you specify an invalid combination.
Further, amongst valid combinations, not all options and datasets will give good results.
Our goal is to have sane defaults,
so that the only choice you usually need to make is what {help svm##type:type} and {help svm##kernel:kernel} to use,
but there is no way to give reasonable defaults for all datasets.
The {help svm##libsvmguide:libsvm guide} covers issues in parameter specification.

{* MODEL PARAMS: }
{phang}
{marker type}{...}
{opt type} specifies which SVM model to run.{p_end}
{pmore}{opt c_svc} and {opt nu_svc} perform classification.{p_end}
{pmore2}{depvar} should be a variable containing categories.
If you try to use floating point values with classification you
will find that they are truncated mercilessly to their integer parts,
so you may need to recode your categories first.
If you end up with almost as many classes as observations,
you have probably used a continuous {depvar} and
should use regression instead.{p_end}

{pmore2}Multiclass classification is automatically handled using the {browse "http://en.wikipedia.org/wiki/Multiclass_classification":one-against-one} method.
You do not need to do anything special to invoke it.{p_end}

{pmore}{opt epsilon_svr} and {opt nu_svr} perform regression.{p_end}
{pmore2}{depvar} should be a variable containing continuous values.{p_end}
{pmore2}While you can use this with discrete {depvar}s, it is more common to use it with continuous ones.{p_end}
{pmore2}See {help svm##svr_tutorial:the SVR tutorial} for more details.{p_end}

{marker one_class}{...}
{pmore}{opt one_class} separates outliers from the bulk of the data.{p_end}
{pmore2}{opt one_class} does not take a {depvar} because it is a form of unsupervised learning: all the information it uses is in the predictors themselves.{p_end}
{pmore2}Its predictions give 1 for "data" and -1 for "outlier".{p_end}
{pmore3}{bf:Tip:} You may use the same {varlist} as with the other types:
 this just interprets your {depvar} as one of the {indepvars},
 giving {opt one_class} more information to work with.{p_end}

{pmore}To learn about the {opt nu} variants, see {help svm##nusvm:Chen and Lin's ν-SVM tutorial}.{p_end}

{pmore}{opt type} is case insensitive.{p_end}

{phang}
{marker kernel}{...}
{opt kernel} gives a kernel function to use.
The basic SVM algorithm is based on linear, euclidean, space. Much like {help glm:GLMs},
 {browse "https://en.wikipedia.org/wiki/Kernel_Method":kernels}
 extend the algorithm to nonlinear data.{p_end}
{pmore}
.
Kernels effectively bend a linear space into a non-linear one with a high-dimensional mapping.
See {browse "https://www.youtube.com/watch?v=3liCbRZPrZA"} for a visualization of this process.
{p_end}
{pmore}
The clever part---and the reason why the set of kernels is hardcoded---is that
for certain kernels [CITATION NEEDED] the fit can be done efficiently without
actually constructing the high-dimensional points, as the estimation only cares
scoring coefficients u using the output of the kernel, not the output of the values the kernel
is, in theory, operating upon.{p_end}

{pmore}Kernels available in this implementation are:{p_end}
{pmore2}{opt linear} means the linear kernel you are probably familiar with from {help regress:OLS}: u'*v{p_end}
{pmore2}{opt poly} is (gamma*u'*v + coef0)^degree{p_end}
{pmore2}{opt rbf} stands for Radial Basis Functions, and treats the coefficients as a mean to smoothly approach in a ball, with the form exp(-gamma*|u-v|^2); this kernel tends to be good at [...].{p_end}
{pmore2}{opt sigmoid} is a kernel which bends the linear kernel to fit in -1 to 1, similar to {help logistic} regression: tanh(gamma*u'*v + coef0){p_end}
{pmore2}{opt precomputed} assumes that {depvar} is actually a list of precomputed kernel values. With effort, you can use this to use custom kernels with your data [TODO].{p_end}

{pmore}{opt kernel} is case insensitive.{p_end}

{phang}
{marker degree}{...}
{opt degree} selects the degree of the polynomial used by the {opt poly} kernel.
Setting this too high will result in overfitting. Setting it too low may result in non-convergence.
[TODO: tips about choosing this]

{* TUNING PARAMS: }
{phang}
{marker c}{...}
{opt c} weights (regularizes) the slack variables used in {opt c_svc}, {opt epsilon_svr} and {opt nu_svr}.
If your fit is poor, try cranking this up.
{* XXX NOT TRUE: This will pre-multiply any sample-specific {opt weight}s. }

{phang}
{marker epsilon}{...}
{opt epsilon} is the margin of error allowed by {opt epsilon_svr}.
Larger makes your fit more able to incorporate more observations, but can lead to underfitting.
Smaller can lead to overfitting.

{phang}
{marker nu}{...}
{opt nu} is used in the nu variants.
The nu variants are a reparamaterization of regular SVM which lets you directly tune,
using {opt nu}, the tradeoff between error and computation.
{opt nu} is an upper bound on the fraction of training errors and a lower bound of the
fraction of support vectors, so that [TODO smaller means ... and larger means .....]
See {help svm##nusvm:the ν-SVM tutorial} for details.
{* ..wait... this doesn't make any sense. nu == 0.1 means there are at most 10% (training) errors and at least 10% are support vectors.}{...}
{*      nu = 0.9 means there are at most 90% errors and at least 90% SVs.   you should always choose 0, then, to get perfect prediction and zero memory usage}{...}

{phang}
{marker gamma}{...}
{opt gamma} is used in the non-linear kernels as a scaling factor for the linear part, as seen above.
[TODO: tips about choosing this]

{phang}
{marker coef0}{...}
{opt coef0} similarly is used in the non-linear kernels as a pseudo-intercept term.
Except it is not used in the {opt rbf} kernel as {opt rbf} is essentially a distance function, and biasing would be pointless.[???]
[TODO: tips about choosing this]

{phang}
{marker shrinking}{...}
{opt shrinking} invokes the shrinkage heuristics [TODO: what does this mean? shrinkage estimation?],
which reduce can improve the fit by trading bias for variance: by giving up accuracy in the estimates,
variance of the estimates can be reduced, which is sometimes better overall.

{* FEATURE PARAMS: }{...}
{* {marker normalize}{...} }
{* {phang} }{...}
{* {opt normalize} instructs the estimation to first center and scale the data }{...}
{* as SVM tends to be very sensitive to scaling issues. }{...}
{* This normalizes all data to [0,1] using min-max normalization, as suggested in the {help svm##libsvmguide:libsvm guide}. }{...}
{* Normalization creates temporary variables, so you may prefer to preprocess the data yourself---destructively and in-place---to save time on re-estimations and memory for variables, }{...}
{* especially if you are bumping up against your Stata system limits. You may find {cmd:ssc install center} helpful }{...}

{phang}
{marker probability}{...}
{opt probability} enables the use of "{help svm##predict_prob:predict, prob}".
That does {browse "https://en.wikipedia.org/wiki/Platt_scaling":Platt scaling},
so for each class-against-class this precomputes a logistic regression
which is tuned with 5-fold cross-validation,
hence enabling this takes a great deal of additional CPU and RAM.

{phang}
{marker sv}{...}
{opt sv} records in the given variable a boolean indicating whether each observation was a support vector or not.

{* PERFORMANCE PARAMS: }
{phang}
{marker tolerance}{...}
{opt tolerance} is the stopping tolerance used by the numerical optimizer. You could widen this if you are finding convergence is slow,
 but be aware that this usually non-convergence is a deeper problem in the interaction of data, kernel, and tuning parameters.

{phang}
{marker verbose}{...}
{opt verbose} enables output from the low level libsvm code for the duration of the operation.

{phang}
{marker cache_size}{...}
{opt cache_size} controls a time-memory tradeoff during estimation.
Value is how many megabytes (MB) of RAM to set aside for caching data points as the optimizer is iterated.
Generally, more is faster, at least until you run out of RAM or cause your machine to start swapping.

{...}
{...}

{marker predict}{...}
{dlgtab:predict}

{pstd}After training you can ask svm to {cmd:predict} what the category (classification) or outcome value (regression)
      should be for each given observation. Results are placed into {newvar}.{p_end}
{pstd}{newvar} must not exist, so if you want to repredict your choices are {cmd:drop {newvar}} or to pick a new name, e.g. {cmd:predict {newvar}2}.{p_end}

{marker predict_prob}{...}
{phang}For classification ({opt c_svc}, {opt nu_svc}) problems, {opt probability} requests, for each observation, the probability of it being each class.
{newvar} is used as a stem for the new columns.
This option is not valid for other SVM types.{p_end}

{phang}
{opt verbose}: see {help svm##verbose:svm, verbose}.

{pstd}
Prediction implicitly uses the same {indepvars} as during estimation, so be careful about renaming or dropping variables.
{...}
{...}

{marker export}{...}
{dlgtab:import/export}

{pstd}
libsvm has an ad-hoc format it uses to save trained models.
The command line programs {cmd:svm-train} and {cmd:svm-predict} which come with libsvm communicate via it.
We support this format for interoperability.

{pstd}
{cmd:svm_export} will write a fitted model to disk.
This can only be run if there is one in memory, either from {help svm##svm:svm} or {help svm##export:svm_import}.
Conventionally you should name your file with '.model' for a file extension.

{pstd}
{cmd:svm_import} will load a model from disk, replacing any previous in-memory fit.
When you import, [TODO: some properties] will be missing because the import was done without reference to any dataset.

{phang}
{opt verbose}: see {help svm##verbose:svm, verbose}.

{pstd}
Do not confuse these commands with {cmd:import_svmlight} and {cmd:export_svmlight}.

{marker examples}{...}
{title:Examples:  classification}

        {hline}
{phang2}{cmd:. webuse highschool, clear}{p_end}
{phang2}{cmd:. svm gender}{p_end}
{phang2}{cmd:. ereturn list}{p_end}
{phang2}{cmd:. predict P, prob}{p_end}
        {hline}

{title:Examples:  regression, suppressing sv_coef}

        {hline}
{phang2}{cmd:. sysuse auto, clear}{p_end}
        {hline}

{title:Examples:  classification with tuning}

        {hline}
{phang2}{cmd:. webuse regsmpl, clear}{p_end}
        {hline}

{title:Examples:  viewing support vectors}

--- crosstab sv against category and compare to nSV

{title:Examples:  forecasting}

        {hline}
--- train on one set of data, generate more observations, predict
        {hline}

{title:Examples:  custom kernel}

        {hline}
--- use hacks + kernel(precomputed)
        {hline}

{marker results}{...}
{title:Stored results}

{pstd}
{cmd:svm} and {cmd:svm_import} stores the following in {cmd:e()}:

{synoptset 20 tabbed}{...}
{* Note: svm_model components left unexposed: }{...}
{*      - SV, the actual SVs found. If you want this in a new fit, you can use the sv() option; the use case of inspecting old SVs on an imported fit is rare, we assume, and we do not support it; (they will still be loaded to memory }{...}
{*      - sv_indices, the indexes of which support vectors were found; exposed indirectly with the sv() option }{...}
{*      - probA, probB, the coefficients used for predict, prob; these are not, by themselves, interesting }{...}
{*      - label, the "labels" of the classes (which are the integers libsvm casts out of the initial dataset; exported with strLabels to be used for labelling rho and sv_coef, but otherwise not directly interesting and  }{...}
{*      - nSV, the number of SVs per class; this is only interesting for classifications, and it duplicates what you can get out of "tab `e(depvar)' SV" }{...}
{*      - free_sv, internal libsvm flag which is a hack to stretch svm_model to handle creation from both svm_train() and svm_import() }{...}
{p2col 5 20 24 2: Scalars}{p_end}
{synopt:{cmd:e(N)}}number of observations{p_end}
{synopt:{cmd:e(N_class)}}number of classes, in a classification problem. {opt 2} in a regression problem.{p_end}
{synopt:{cmd:e(N_SV)}}number of support vectors.
If {opt e(N_SV)}/{opt e(N)} is close to 100% your fit is inefficient; perhaps you need to adjust your {help svm##kernel:kernel}.
{p_end}

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Macros}{p_end}
{synopt:{cmd:e(cmd)}}"{cmd:svm}"{p_end}
{synopt:{cmd:e(cmdline)}}command as typed{p_end}
{synopt:{cmd:e(depvar)}}name of dependent variable{p_end}
{synopt:{cmd:e(title)}}title in estimation output{p_end}
{synopt:{cmd:e(model)}}"{cmd:svm}"{p_end}
{synopt:{cmd:e(svm_type)}}SVM type string, as above{p_end}
{synopt:{cmd:e(svm_kernel)}}kernel string, as above{p_end}
{synopt:{cmd:e(predict)}}program used to implement {cmd:predict}{p_end}
{synopt:{cmd:e(levels)}}list of the classes detected, in the order they were detected, if applicable{p_end}
{* {synopt:{cmd:e(estat_cmd)}}program used to implement {cmd:estat}{p_end} }{...}

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Matrices}({help svm##gotchas:may be missing}){p_end}
{synopt:{cmd:e(sv_coef)}}The coefficients of the support vectors for each fitted hyperplane. [TODO]{p_end}
{synopt:{cmd:e(rho)}}The intercept term for each fitted hyperplane. It is lower-triangular and {cmd:e(N_class)}^2 large, with each entry [i,j] representing the hyperplane between class i and class j.{p_end}

{* XXX this section title should be more formal}{...}
{marker gotchas}{...}
{title:Gotchas}

{pstd}
{bf:Memory Limits}: The cheaper versions of Stata allow only allow less variables and smaller matrices to be used.
As machine learning problems typically are on very large datasets,
it is easy to inadvertently instruct this package to construct more columns or larger matrices than you can afford.
If you overflow {help maxvar}, you will receive an error, the operation will fail, and the dataset will be left untouched.
If you overflow {help matsize}, the matrix that overflowed will be missing, but operation will otherwise succeed.

{pmore}
If Stata's memory limits are an impossible hurdle,
your best option is to give up on Stata and switching to libsvm's companion {cmd:svm-train} program.
This will have been installed with the libsvm package if you used a package manager, or
you can get it {browse "http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/libsvm.cgi?+http://www.csie.ntu.edu.tw/~cjlin/libsvm+zip":from its authors};
You can use {help svmlight:export svmlight} to extract your dataset for use with {cmd:svm-train}.

{marker copyright}{...}
{title:Copyright}

{pstd}
The wrapper is licensed:

{pmore}
The MIT License (MIT)

{pmore}
Except where otherwise noted in the code, Copyright (c) 2015
Nick Guenther <nguenthe@uwaterloo.ca>, Matthias Schonlau <schonlau@uwaterloo.ca>

{pmore}
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

{pmore}
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

{pmore}
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

{pstd}
libsvm is licensed:

{pmore}
Copyright (c) 2000-2014 Chih-Chung Chang and Chih-Jen Lin
All rights reserved.

{pmore}
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

{pmore}
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

{pmore}
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

{pmore}
3. Neither name of copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

{pmore}
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

{marker authors}{...}
{title:Authors}

{pstd}
Though the license does not obligate you in any way to do so, if you find
this software useful we would be curious and appreciative to hear about your
adventures in machine learning with Stata.{p_end}
{pmore}Thank you.

{pstd}You can contact us at{p_end}
{pmore}* Nick Guenther <nguenthe@uwaterloo.ca>{p_end}
{pmore}* Matthias Schonlau <schonlau@uwaterloo.ca>{p_end}

{marker references}{...}
{title:References}

{marker sourcecode}{...}
{phang}
Guenther, Nick and Schonlau, Matthias. 2015.
{it:Stata-SVM}.
{browse "https://github.com/kousu/statasvm/"}.
{p_end}

{marker svmtutorial}{...}
{phang}
Bennett, Kristin P., and Colin Campbell. 2000.
{it:Support Vector Machines: Hype or Hallelujah?}
SIGKDD Explor. Newsl. 2.2: 1–13.
{browse "http://www.svms.org/tutorials/BennettCampbell2000.pdf"}.
{p_end}

{marker libsvm}{...}
{phang}
Chang, Chih-Chung and Lin, Chih-Jen. 2011.
{it:LIBSVM: a library for support vector machines.}
ACM Transactions on Intelligent Systems and Technology, 2:27:1--27:27.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/libsvm.pdf"}.
Software available at {browse "http://www.csie.ntu.edu.tw/~cjlin/libsvm"}
{p_end}

{marker libsvmguide}{...}
{phang}
Hsu, Chih-Wei, Chang, Chih-Chung, and Lin, Chih-Jen. April 15, 2010.
{it:A Practical Guide to Support Vector Classification}.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf"}.
{p_end}

{marker svr_tutorial}{...}
{phang}
Smola, Alex J., and Schölkopf, Bernhard. 2004.
{it:A tutorial on support vector regression}.
Statistics and Computing 14.3: 199–222.
{* This one is behind a paywall, so the best we can do is a give a DOI link }{...}
{browse "http://dx.doi.org/10.1023/b:stco.0000035301.49549.88"}.
{p_end}

{marker nusvm}{...}
{phang}
Chen, Pai-Hsuen, Lin Chih-Jen, and Schölkopf, Bernhard. 2005.
{it:A Tutorial on ν-Support Vector Machines}.
Applied Stochastic Models in Business and Industry 21.2: 111–136.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/nusvmtutorial.pdf"}.
{p_end}


consolidate into variables

consolidate into variables

* the world is crumbling. solutions are being introduced. they wont pass without radical political change
* 1 billion divided equally between states
* 1 billion distributed on the basis of relative 16-25 y/o compared to total in all states
* 1 billion on relative unemployed compared to toal in all states
* 1 billion on the basis of the rleative # disadvantaged youth compared to total in all states

*STEPS:
* # of 16-25 y/o's per state
* total # of 16-25 y/os
* # of unemployed per state
* total # unemployed
* # disdvantaged youth
* total # disadvantaged youth

*then by puma w/in state
*
*tabulate age if statefip == 06 & puma == 3703 [fweight=perwt]
gen youth = (age <= 25 & age >= 16)
gen disadvantaged_youth = (youth & (poverty <= 125))
gen unemployed = (empstat == 2)

egen youth_state = total(perwt * youth), by(statefip)
egen youth_puma = total(perwt * youth), by(statefip puma)
*sum youth_puma if statefip == 06 & puma == 3703
*25371 youth in lancaster
gen youth_percentage = youth_puma / youth_state

egen unemployed_state = total(perwt * unemployed), by(statefip)
egen unemployed_puma = total(perwt * unemployed), by(statefip puma)

gen unemployed_percentage = unemployed_puma / unemployed_state

*sum youth_puma if statefip == 06 & puma == 3703
*5797 youth in lancaster

egen disadvantaged_state = total(perwt * disadvantaged_youth), by(statefip)
egen disadvantaged_puma = total(perwt * ((age <= 25 & age >= 16) & poverty <= 125)), by(statefip puma)

gen disadvantaged_percentage = disadvantaged_puma / disadvantaged_state

egen youth_total = total(perwt * (age <= 25 & age >= 16))
egen unemployed_total = total(perwt * (empstat == 2))
egen disadvantaged_total = total(perwt * ((age <= 25 & age >= 16) & poverty <= 125))

gen youth_percentage_state = youth_state/youth_total
gen unemployed_percentage_state = unemployed_state/unemployed_total
gen disadvantaged_percentage_state = disadvantaged_state/disadvantaged_total

gen state_money = 1000 * .02 + 1000 * youth_percentage_state + 1000 * unemployed_percentage_state + 1000 * disadvantaged_percentage_state

egen npumas = nvals(puma), by(statefip)

gen puma_money = (state_money/3) * (youth_percentage + unemployed_percentage + disadvantaged_percentage)
gen numjobs = puma_money / .0105 //.0105 is a 10,500 dollar per yr job

*figure out households w/ youth in them
egen householdwithyouth = max(age >= 16 & age <= 26), by(serial)
*youth hh's living in poverty
gen youthinpov = (householdwithyouth & (poverty <= 100))
tab youthinpov if pernum == 1 [fweight = perwt]
*162 jobs for 5287 households

gen job_money = 0
replace job_money = 10500 if youthinpov & pernum == 1
gen newincome = inctot+job_money

sgini newincome if pernum == 1 [fweight=hhwt]
sgini inctot if pernum == 1 [fweight=hhwt]

* the world is crumbling. solutions are being introduced. they wont pass without radical political change
* 1 billion divided equally between states
* 1 billion distributed on the basis of relative 16-25 y/o compared to total in all states
* 1 billion on relative unemployed compared to toal in all states
* 1 billion on the basis of the rleative # disadvantaged youth compared to total in all states

*STEPS:
* # of 16-25 y/o's per state
* total # of 16-25 y/os
* # of unemployed per state
* total # unemployed
* # disdvantaged youth
* total # disadvantaged youth

*then by puma w/in state
*
*tabulate age if statefip == 06 & puma == 3703 [fweight=perwt]
egen youth_state = total(perwt * (age <= 25 & age >= 16)), by(statefip)
egen youth_puma = total(perwt * (age <= 25 & age >= 16)), by(statefip puma)
*sum youth_puma if statefip == 06 & puma == 3703
*25371 youth in lancaster
gen youth_percentage = youth_puma / youth_state

egen unemployed_state = total(perwt * (empstat == 2)), by(statefip)
egen unemployed_puma = total(perwt * (empstat == 2)), by(statefip puma)

gen unemployed_percentage = unemployed_puma / unemployed_state

*sum youth_puma if statefip == 06 & puma == 3703
*5797 youth in lancaster

egen disadvantaged_state = total(perwt * ((age <= 25 & age >= 16) & poverty <= 125)), by(statefip)
egen disadvantaged_puma = total(perwt * ((age <= 25 & age >= 16) & poverty <= 125)), by(statefip puma)

gen disadvantaged_percentage = disadvantaged_puma / disadvantaged_state

egen youth_total = total(perwt * (age <= 25 & age >= 16))
egen unemployed_total = total(perwt * (empstat == 2))
egen disadvantaged_total = total(perwt * ((age <= 25 & age >= 16) & poverty <= 125))

gen youth_percentage_state = youth_state/youth_total
gen unemployed_percentage_state = unemployed_state/unemployed_total
gen disadvantaged_percentage_state = disadvantaged_state/disadvantaged_total

gen state_money = 1000 * .02 + 1000 * youth_percentage_state + 1000 * unemployed_percentage_state + 1000 * disadvantaged_percentage_state

egen npumas = nvals(puma), by(statefip)

gen puma_money = (state_money/3) * (youth_percentage + unemployed_percentage + disadvantaged_percentage)
gen numjobs = puma_money / .0105 //.0105 is a 10,500 dollar per yr job

*figure out households w/ youth in them
egen householdwithyouth = max(age >= 16 & age <= 26), by(serial)
*youth hh's living in poverty
gen youthinpov = (householdwithyouth & (poverty <= 100))
tab youthinpov if pernum == 1 [fweight = perwt]
*162 jobs for 5287 households

gen job_money = 0
replace job_money = 10500 if youthinpov & pernum == 1
gen newincome = inctot+job_money

sgini newincome if pernum == 1 [fweight=hhwt]
sgini inctot if pernum == 1 [fweight=hhwt]


working on hetero effects

working on hetero effects

clear all
set more off
set matsize 11000
global path "/mnt/nfs6/wikipedia.proj/jmp/jmpscripts/stata/"
global rawosm "/mnt/nfs6/wikipedia.proj/jmp/rawdata/osm/"
global osmchange "/mnt/nfs6/wikipedia.proj/jmp/rawdata/osmchange/"
global rawmaps "/mnt/nfs6/wikipedia.proj/jmp/rawdata/maps/"
global rawtrips "/mnt/nfs6/wikipedia.proj/jmp/rawdata/trips/"
global stash "/mnt/nfs6/wikipedia.proj/jmp/rawdata/stash/"
global myestimates "/mnt/nfs6/wikipedia.proj/jmp/jmpscripts/stata/estimates/"
global tables "/mnt/nfs6/wikipedia.proj/jmp/jmpscripts/stata/tables/"
 global rawhist "/mnt/nfs6/wikipedia.proj/jmp/rawdata/osmhistory/"

adopath + "/mnt/nfs6/wikipedia.proj/jmp/jmpscripts/stata/ado"

cd ${path}

program drop _all

 // STEP 0 -- clean 3 datasets (change, msa, county)
 // TODO: add tileid logic

 // Step 1.2 -- create outcome variables
 // Step 1.3 -- fills in blanks and xtset the data

 // for geoid10-fips
 use ${stash}mergemaster1, clear
 makedv "fips geoid10"
 balancepanel geoid10
 save ${stash}panelfips_geoid10, replace

 // just geoid
 use ${stash}mergemaster1, clear
 drop if geoid10 == "NA"
 bysort geoid10 fips: gen tmp = _n==1
 gen treattmp = treat
 replace treattmp = . if tmp == 0
 bysort geoid10: egen avgtreat = mean(treattmp)
 replace treat = avgtreat
 drop treattmp tmp avgtreat
 makedv "geoid10"
 balancepanel geoid10
 save ${stash}panelgeoid10, replace

 ** Analysis

// 0. Data

// 0.1 preprep
preparebasic
mergebasic

// 0.2 make fips dataset
use ${stash}mergemaster1, clear
makedv fips
balancepanel fips
save ${stash}panelfips, replace

// 0.3 make node dataset

makedv fips

// TODO: makevar
// TODO: balancepanel

// Analysis

// 1. Summary stats
use ${stash}panelfips, clear
makesummary

// 2. Treatment vs. control charts
use ${stash}panelfips, clear
makehist cntypop kdensity
makehist region hist

// 3. Baseline effects (xtpoisson, cluster at unit level)

//  3.1 Meanline charts (raw data)
makemeanline numchanges quarter 2011 "Changes"
makemeanline numcontrib quarter 2011 "Contributions"
makemeanline numuser quarter 2011 "Users"
makemeanline numserious90 quarter 2011 "Super Users"
makemeanline numnewusers quarter 2011 "New Users"
makemeanline numnewusers6 quarter 2011 "New Users (Stay for 6+ Months)"
makemeanline numnewusers90 quarter 2011 "New Users (who become super users)"

// 3.2 FIPS Sample -- DD

/// 3.2.1 -- contrib, users, superu
program drop _all

clear
local dv "numcontrib numuser numserious90"
local unit fips
rundd panelfips `unit' "`dv'"
diffindiff xtpoisson "`dv'" `unit' 2014 write tab_3.2.1 "Contributions" "Users" "Super Users"

/// 3.2.2 -- newusers, newusers6, newuserssuper
clear
local dv "numnewusers numnewusers6 numnewusers90"
local unit fips
rundd panelfips `unit' "`dv'"
diffindiff xtpoisson "`dv'" `unit' 2014 write tab_3.2.2 "New Users" "New Users(6+)" "New Super Users"

/// 3.2.3 -- streetcontrib, nonstreet contrib, amenities
// TODO

/// 3.2.4 -- completeness
// TODO

// 3.2.5 -- Diff in Diff charts
// TODO : make this more flexible
// TODO : this works only for fips
ddchart

// 3.3 Robustness : Drop empty counties
// TODO
//  e. Repeat (a) by dropping empty counties
//  f. Repeat (a) by dropping west
//  g. Repeat (a) for Tile Sample, MSA sample
//  h. Repeat (a) with matched estimators
//  i. Repeat (a) with spatially clustered se, Region X Time trends

// 4 Heterogenous Effects

// 1. Large vs. small
use ${stash}panelfips, clear
gen large = (cntypop > 100000)

program drop _all

diffindiff2 large LARGE tab_4.1 numcontrib numuser numnewusers run

diffindiff2 large LARGE tab_4.1 numcontrib numuser numserious90 run

diffindiff2 large LARGE tab_4.1 numco numuser numserious90 write

// 1. engaged vs. non-engaged county
// 2. urban vs rural
// 3. university vs non university
// 4. rich vs poor

// 5 Person Level Regressions

// [A] outcome vars: County sample -- DD spec

   // a. Contributions
   // b. Street, Non Street
   // b. TIGER vs. Non Tiger
   // d. Home county, Num Counties

// [B] heterogenous

   // a. Junior vs Senior

// 6 Impacts on Navigation

// PENDING

// GEOID Level

// TASKS for tomorrow

// STASH

 // 1.1 Mean Charts

 // 1.2 Produce Diff in diff Latex tables
 // rundd -> batchreg.sh -> runreg.do -> diffindiff.ado
 program drop _all

 // TODO: fix manual process
 // have to do this manually

 // 1.3 Produce Diff in diff Pictures

 ** person level regressions
 // person level
 makeperson
 ddperson maketables
 ddperson makechart

// TODOs for Tuesday Jun 3
// GEOID dataset
// TILEID diff in diff
// What happened to previous people?
// What happened at the street/amenity level?
// Urban vs Rural (framework for third level diff)

// 1.4 Robust: product diff in diff for GEOID

use ${stash}panelfips_geoid10, clear

** Analysis Stage 2
//

// PROGRAMS

clear all
set more off
set matsize 11000
global path "/mnt/nfs6/wikipedia.proj/jmp/jmpscripts/stata/"
global rawosm "/mnt/nfs6/wikipedia.proj/jmp/rawdata/osm/"
global osmchange "/mnt/nfs6/wikipedia.proj/jmp/rawdata/osmchange/"
global rawmaps "/mnt/nfs6/wikipedia.proj/jmp/rawdata/maps/"
global rawtrips "/mnt/nfs6/wikipedia.proj/jmp/rawdata/trips/"
global stash "/mnt/nfs6/wikipedia.proj/jmp/rawdata/stash/"
global myestimates "/mnt/nfs6/wikipedia.proj/jmp/jmpscripts/stata/estimates/"
global tables "/mnt/nfs6/wikipedia.proj/jmp/jmpscripts/stata/tables/"
 global rawhist "/mnt/nfs6/wikipedia.proj/jmp/rawdata/osmhistory/"

adopath + "/mnt/nfs6/wikipedia.proj/jmp/jmpscripts/stata/ado"

cd ${path}

program drop _all

 // STEP 0 -- clean 3 datasets (change, msa, county)
 // TODO: add tileid logic

 // Step 1.2 -- create outcome variables
 // Step 1.3 -- fills in blanks and xtset the data

 // for geoid10-fips
 use ${stash}mergemaster1, clear
 makedv "fips geoid10"
 balancepanel geoid10
 save ${stash}panelfips_geoid10, replace

 // just geoid
 use ${stash}mergemaster1, clear
 drop if geoid10 == "NA"
 bysort geoid10 fips: gen tmp = _n==1
 gen treattmp = treat
 replace treattmp = . if tmp == 0
 bysort geoid10: egen avgtreat = mean(treattmp)
 replace treat = avgtreat
 drop treattmp tmp avgtreat
 makedv "geoid10"
 balancepanel geoid10
 save ${stash}panelgeoid10, replace

 ** Analysis

// 0. Data

// 0.1 preprep
preparebasic
mergebasic

// 0.2 make fips dataset
use ${stash}mergemaster1, clear
makedv fips
balancepanel fips
save ${stash}panelfips, replace

// 0.3 make node dataset

makedv fips

// TODO: makevar
// TODO: balancepanel

// Analysis

// 1. Summary stats
use ${stash}panelfips, clear
makesummary

// 2. Treatment vs. control charts
use ${stash}panelfips, clear
makehist cntypop kdensity
makehist region hist

// 3. Baseline effects (xtpoisson, cluster at unit level)

//  3.1 Meanline charts (raw data)
makemeanline numchanges quarter 2011 "Changes"
makemeanline numcontrib quarter 2011 "Contributions"
makemeanline numuser quarter 2011 "Users"
makemeanline numserious90 quarter 2011 "Super Users"
makemeanline numnewusers quarter 2011 "New Users"
makemeanline numnewusers6 quarter 2011 "New Users (Stay for 6+ Months)"
makemeanline numnewusers90 quarter 2011 "New Users (who become super users)"

// 3.2 FIPS Sample -- DD

/// 3.2.1 -- contrib, users, superu
program drop _all

clear
local dv "numcontrib numuser numserious90"
local unit fips
rundd panelfips `unit' "`dv'"
diffindiff xtpoisson "`dv'" `unit' 2014 write tab_3.2.1 "Contributions" "Users" "Super Users"

/// 3.2.2 -- newusers, newusers6, newuserssuper
clear
local dv "numnewusers numnewusers6 numnewusers90"
local unit fips
rundd panelfips `unit' "`dv'"
diffindiff xtpoisson "`dv'" `unit' 2014 write tab_3.2.2 "New Users" "New Users(6+)" "New Super Users"

/// 3.2.3 -- streetcontrib, nonstreet contrib, amenities
// TODO

/// 3.2.4 -- completeness
// TODO

// 3.2.5 -- Diff in Diff charts
// TODO : make this more flexible
// TODO : this works only for fips
ddchart

// 3.3 Robustness : Drop empty counties
// TODO
//  e. Repeat (a) by dropping empty counties
//  f. Repeat (a) by dropping west
//  g. Repeat (a) for Tile Sample, MSA sample
//  h. Repeat (a) with matched estimators
//  i. Repeat (a) with spatially clustered se, Region X Time trends

// 4 Heterogenous Effects

// 1. Large vs. small
use ${stash}panelfips, clear
gen large = (cntypop > 100000)

program drop _all

diffindiff2 large LARGE tab_4.1 numco numuser numnewusers run

diffindiff2 large LARGE tab_4.1 numco numuser numnewusers write

// 1. engaged vs. non-engaged county
// 2. urban vs rural
// 3. university vs non university
// 4. rich vs poor

// 5 Person Level Regressions

// [A] outcome vars: County sample -- DD spec

   // a. Contributions
   // b. Street, Non Street
   // b. TIGER vs. Non Tiger
   // d. Home county, Num Counties

// [B] heterogenous

   // a. Junior vs Senior

// 6 Impacts on Navigation

// PENDING

// GEOID Level

// TASKS for tomorrow

// STASH

 // 1.1 Mean Charts

 // 1.2 Produce Diff in diff Latex tables
 // rundd -> batchreg.sh -> runreg.do -> diffindiff.ado
 program drop _all

 // TODO: fix manual process
 // have to do this manually

 // 1.3 Produce Diff in diff Pictures

 ** person level regressions
 // person level
 makeperson
 ddperson maketables
 ddperson makechart

// TODOs for Tuesday Jun 3
// GEOID dataset
// TILEID diff in diff
// What happened to previous people?
// What happened at the street/amenity level?
// Urban vs Rural (framework for third level diff)

// 1.4 Robust: product diff in diff for GEOID

use ${stash}panelfips_geoid10, clear

** Analysis Stage 2
//

// PROGRAMS


typos in output

typos in output

	*! version 3.0 26DEC2016  Kristoffer Bjarkefur kbjarkefur@worldbank.org

	capture program drop iecompdup
	program iecompdup , rclass

	qui {

		syntax varname ,  id(string) [DIDIfference KEEPDIFFerence KEEPOTHer(varlist) more2ok]

		version 11.0

		preserve

			/****************************

				Turn ID var to string so that the rest of the command works similarly

			****************************/

			* Test if ID variable is numeric or string
			cap confirm numeric variable `varlist'
			if !_rc {

				* If numeric, test if all values in ID variable is integer
				cap assert mod(`varlist',1) == 0

				* This command does not allow numeric ID varaibels that are not integers
				if _rc {
					di as error "{phang}The ID variable is only allowed to be either string or only consist of integers. Integer in this context is not the same as the variable type int. Integer in this context means numeric values without decimals. Please consider using integers as your ID or convert your ID variable to a string.{p_end}"

				}
				else {

					** Find the longest (for integers that is the same as largest)
					*  number and get its legth.l
					sum `varlist'
					local length 	= strlen("`r(max)'")

					** Use that length when explicitly setting the format in
					*  order to prevent information lost
					tostring `varlist', replace format(%`length'.0f)
				}
			}

			* Testing that the ID variable is a string before constinueing the command
			cap confirm string variable `varlist'
			if _rc {
				di as error "{phang}This error message is not due to incorrect specification from you. This message follows a failed check that the command is working properly. If you get this error, please send an email to kbjarkefur@worldbank.org including the follwoing message 'ID var was not succesfully turned in to a string without information loss in iecompdup.' and include whatever other data you do not mind sharing.{p_end}"
			}

			* Only keep duplicates with the ID specified
			keep if  `varlist' == "`id'"

			/****************************

				Test if input is correct

			****************************/

			if "`keepdifference'" == "" & "`keepother'" != "" {

				noi di as error "{phang}Not allowed to specify keepother() without specifying keepdifference{p_end}"
				noi di ""
				error 197
				exit
			}

			* Test the number of observations left and make sure that there are only two of them
			count
			if `r(N)' == 0 {

				noi di as error "{phang}ID incorrectly specified. No observations with (`varlist' == `id'){p_end}"
				noi di ""
				error 2000
				exit
			}
			else if `r(N)' == 1 {

				noi di as error "{phang}ID incorrectly specified. No duplicates with that ID. Only one observation where (`varlist' == `id'){p_end}"
				noi di ""
				error 2001
				exit
			}
			else if `r(N)' > 2 & "`more2ok'" == "" {

				noi di as error "{phang}The current version of ie_compdup is not able to compare more than 2 duplicates at the time. (How to output the results for groups larger than 2 is non-obvious and suggestions on how to do that are appreciated.) Either drop one of the duplicates before re-running the command or specify option more2ok and the comparison will be done between the first and the second row.{p_end}"
				noi di ""
				error 198
				exit
			}
			else {

			/****************************

				Compare all variables

			****************************/

				* If more than 2 observations, keep the first and second row only
				keep if _n <= 2

				*Initiate the locals
				local match
				local difference

				* Go over all variables and see if they are non missing for at least one of the variables
				foreach var of varlist _all {

					cap assert missing(`var')

					** If not missing for at lease one of the observations, test
					*  if they are identical across the duplicates or not, and
					*  store variable name in appropriate local
					if _rc {

						* Are the variables identical
						if `var'[1] == `var'[2] {
							local match `match' `var'
						}
						else {
							local difference `difference' `var'
						}
					}
					* If missing for all duplicates, then drop that variable
					else {
						drop `var'
					}
				}

				* Remove the ID var from the match list, it is match by definition and therefore add no information
				local match : list match - varlist

			/****************************

				Output the result

			****************************/

				noi di ""
				** Display all variables that differ. This comes first in case
				*  the number of variables are a lot, cause then it would push
				*  any other output to far up
				if "`didifference'" != "" {

					noi di "{phang}The following variables have different values across the duplicates:{p_end}"
					noi di "{pstd}`difference'{p_end}"
					noi di ""
				}

				*Display number output
				local numNonMissing = `:list sizeof match' + `:list sizeof difference'

				noi di "{phang}The duplicate observations with ID = `id' have non-missing values in `numNonMissing' variables. Out of those variables:{p_end}"
				noi di ""
				noi di "{phang2}`:list sizeof match' variable(s) are identical across the duplicates{p_end}"
				noi di "{phang2}`:list sizeof difference' variable(s) have different values across the duplicates{p_end}"

				return local matchvars 	`"`match'"'
				return local diffvars 	`"`difference'"'

				return scalar nummatch	= `:list sizeof match'
				return scalar numdiff 	= `:list sizeof difference'
				return scalar numnomiss	= `numNonMissing'
		}

		restore

		* If keep difference is applied only keep those variables here.
		if "`keepdifference'" != "" & "`difference'" != "" {

			order `varlist' `difference' `keepother'
			keep `varlist' `difference' `keepother'

			* Drop differently depending on numeric or string
			cap confirm numeric variable `varlist'
			if !_rc {
				keep if  `varlist' == `id'
			}
			else {
				keep if  `varlist' == "`id'"
			}
		}
	}
	end

	*! version 3.0 26DEC2016  Kristoffer Bjarkefur kbjarkefur@worldbank.org

	capture program drop iecompdup
	program iecompdup , rclass

	qui {

		syntax varname ,  id(string) [DIDIfference KEEPDIFFerence KEEPOTHer(varlist) more2ok]

		version 11.0

		preserve

			/****************************

				Turn ID var to string so that the rest of the command works similarly

			****************************/

			* Test if ID variable is numeric or string
			cap confirm numeric variable `varlist'
			if !_rc {

				* If numeric, test if all values in ID variable is integer
				cap assert mod(`varlist',1) == 0

				* This command does not allow numeric ID varaibels that are not integers
				if _rc {
					di as error "{phang}The ID variable is only allowed to be either string or only consist of integers. Integer in this context is not the same as the variable type int. Integer in this context means numeric values without decimals. Please consider using integers as your ID or convert your ID variable to a string.{p_end}"

				}
				else {

					** Find the longest (for integers that is the same as largest)
					*  number and get its legth.l
					sum `varlist'
					local length 	= strlen("`r(max)'")

					** Use that length when explicitly setting the format in
					*  order to prevent information lost
					tostring `varlist', replace format(%`length'.0f)
				}
			}

			* Testing that the ID variable is a string before constinueing the command
			cap confirm string variable `varlist'
			if _rc {
				di as error "{phang}This error message is not due to incorrect specification from you. This message follows a failed check that the command is working properly. If you get this error, please send an email to kbjarkefur@worldbank.org including the follwoing message 'ID var was not succesfully turned in to a string without information loss in iecompdup.' and include whatever other data you do not mind sharing.{p_end}"
			}

			* Only keep duplicates with the ID specified
			keep if  `varlist' == "`id'"

			/****************************

				Test if input is correct

			****************************/

			if "`keepdifference'" == "" & "`keepother'" != "" {

				noi di as error "{phang}Not allowed to specify keepother() without specifying keepdifference{p_end}"
				noi di ""
				error 197
				exit
			}

			* Test the number of observations left and make sure that there are only two of them
			count
			if `r(N)' == 0 {

				noi di as error "{phang}ID incorrectly specified. No observations with (`varlist' == `id'){p_end}"
				noi di ""
				error 2000
				exit
			}
			else if `r(N)' == 1 {

				noi di as error "{phang}ID incorrectly specified. No duplicates with that ID. Only one observation where (`varlist' == `id'){p_end}"
				noi di ""
				error 2001
				exit
			}
			else if `r(N)' > 2 & "`more2ok'" == "" {

				noi di as error "{phang}The current version of ie_compdup is not able to compare more than 2 duplicates at the time. (How to output the results for groups larger than 2 is non-obvious and suggestions on how to do that are appreciated.) Either drop one of the duplicates before re-running the command or specify option more2ok and the comparison will be done between the first and the second row.{p_end}"
				noi di ""
				error 198
				exit
			}
			else {

			/****************************

				Compare all variables

			****************************/

				* If more than 2 observations, keep the first and second row only
				keep if _n <= 2

				*Initiate the locals
				local match
				local difference

				* Go over all variables and see if they are non missing for at least one of the variables
				foreach var of varlist _all {

					cap assert missing(`var')

					** If not missing for at lease one of the observations, test
					*  if they are identical across the duplicates or not, and
					*  store variable name in appropriate local
					if _rc {

						* Are the variables identical
						if `var'[1] == `var'[2] {
							local match `match' `var'
						}
						else {
							local difference `difference' `var'
						}
					}
					* If missing for all duplicates, then drop that variable
					else {
						drop `var'
					}
				}

				* Remove the ID var from the match list, it is match by definition and therefore add no information
				local match : list match - varlist

			/****************************

				Output the result

			****************************/

				noi di ""
				** Display all variables that differ. This comes first in case
				*  the number of variables are a lot, cause then it would push
				*  any other output to far up
				if "`didifference'" != "" {

					noi di "{phang}The following variables have different values across the duplicates:{p_end}"
					noi di "{pstd}`difference'{p_end}"
					noi di ""
				}

				*Display number output
				local numNonMissing = `:list sizeof match' + `:list sizeof difference'

				noi di "{phang}The duplicate observations with ID = `id' have non-missing values in `numNonMissing' variables. Out of those variables:{p_end}"
				noi di ""
				noi di "{phang2}`:list sizeof match' varaible(s) are identical across the duplicates{p_end}"
				noi di "{phang2}`:list sizeof difference' varaible(s) have different values across the duplicates{p_end}"

				return local matchvars 	`"`match'"'
				return local diffvars 	`"`difference'"'

				return scalar nummatch	= `:list sizeof match'
				return scalar numdiff 	= `:list sizeof difference'
				return scalar numnomiss	= `numNonMissing'
		}

		restore

		* If keep difference is applied only keep those variables here.
		if "`keepdifference'" != "" & "`difference'" != "" {

			order `varlist' `difference' `keepother'
			keep `varlist' `difference' `keepother'

			* Drop differently depending on numeric or string
			cap confirm numeric variable `varlist'
			if !_rc {
				keep if  `varlist' == `id'
			}
			else {
				keep if  `varlist' == "`id'"
			}
		}
	}
	end


Kludge: destroy probA and probB, just in case.

If i'm not going to rename probA->e(probA) (which is what return matrix = does, surprise! it's neither copy nor alias like it would be in every other language and in most other contexts in Stata including return scalar = ), then I should smash it to bits until I decide if I'm keeping it or not.

program _svm, plugin /*load the C extension if not already loaded*/

* subroutine to convert the global struct svm_model that lives in the DLL to a mixture of e() entries, variables, and matrices
* this needs to be its own subroutine because, due to limitations in the Stata C API,th
* it does an awkward dance where _svm.plugin writes to the (global!) scalar dict and then this code copies those entries to r()
* as with svm_load, the extension function is called multiple times with sub-sub-commands, because it doesn't have permission to perform all the operations needed
program define _svm_model2stata, eclass
  version 13

  * as with loading, this has to call in and out of the plugin because chicken/egg:
  *   the plugin doesn't have permission to allocate Stata memory (in this case matrices),
  *   but we don't know how much to allocate before interrogating the svm_model

  local have_sv_indices = 0
  local have_rho = 0
  local have_probA = 0 /*an undefined macro will inconsistently cause an eval error because `have_rho'==1 will eval to ==1 will eval to "unknown variable"*/
  local have_probB = 0 /*so just define them ahead of time to be safe*/

  * Phase 1
  plugin call _svm, "_model2stata" 1

  * the total number of (detected?) classes
  ereturn scalar nr_class = _model2stata_nr_class
  scalar drop _model2stata_nr_class

  * the number of support vectors
  ereturn scalar l = _model2stata_l
  scalar drop _model2stata_l

  * Phase 2
  * Allocate Stata matrices and copy the libsvm matrices and vectors

  *TODO: SV and sv_indices are probably best translated jointly by adding an indicator variable column to the original dataset telling if that observation was chosen as a support vector (of course, this will mean we need to further clamp the upper limit)

  if(`have_sv_indices'==1 & `e(l)'>0) {
    matrix SVs = J(e(l),1,.) /*but for now we can just copy out sv_indices*/
    matrix colnames SVs = "SVs"
  }

  * nSV tells how many support vectors went into each class
  * The sum of the entries in nSV should be l
  matrix nSV = J(e(nr_class),1,.)
  matrix colnames nSV = "nSV"
  /*matrix rownames nSV = . . */

  if(`e(nr_class)'>0) {
    matrix labels = J(e(nr_class),1,.)
    matrix colnames labels = "labels"
  }

  if(`e(nr_class)'>1 & `e(l)'>0) {
    matrix sv_coef = J(e(nr_class)-1,e(l),.)
  }

  if(`have_rho'==1 & `e(nr_class)'>0) {
    matrix rho = J(e(nr_class),e(nr_class),.)
  }
  if(`have_probA'==1 & `e(nr_class)'>0) {
    matrix probA = J(e(nr_class),e(nr_class),.)
  }
  if(`have_probB'==1 & `e(nr_class)'>0) {
    matrix probB = J(e(nr_class),e(nr_class),.)
  }

  * TODO: also label the rows according to model->label (libsvm's "labels" are just more integers, but it helps to be consistent anyway);
  *  I can easily extract ->label with the same code, but attaching it to the rownames of the other is tricky
  plugin call _svm, "_model2stata" 2

  * Phase 3
  * Export the rest of the values to e()
  * We *cannot* export matrices to e() from the C interface, hence we have to do this very explicit thing
  * NOTE: 'ereturn matrix' erases the old name (unless you specify ,copy), which is why we don't have to explicitly drop things
  *       'ereturn scalar' doesn't do this, because Stata loves being consistent. Just go read the docs for 'syntax' and see how easy it is.
  * All of these are silenced because various things might kill any of them, and we want failures to be independent of each other

  quietly capture ereturn matrix SVs = SVs

  quietly capture ereturn matrix nSV = nSV
  quietly capture ereturn matrix labels = labels

  quietly capture ereturn matrix sv_coef = sv_coef
  quietly capture ereturn matrix rho = rho
  //quietly capture ereturn matrix probA = probA //XXX disabled: these are probably not something you care to look at directly
  //quietly capture ereturn matrix probB = probB //              rather, use "predict, prob"; if a compelling reason to expose these comes up we can
  capture matrix drop probA
  capture matrix drop probB

end

program _svm, plugin /*load the C extension if not already loaded*/

* subroutine to convert the global struct svm_model that lives in the DLL to a mixture of e() entries, variables, and matrices
* this needs to be its own subroutine because, due to limitations in the Stata C API,th
* it does an awkward dance where _svm.plugin writes to the (global!) scalar dict and then this code copies those entries to r()
* as with svm_load, the extension function is called multiple times with sub-sub-commands, because it doesn't have permission to perform all the operations needed
program define _svm_model2stata, eclass
  version 13

  * as with loading, this has to call in and out of the plugin because chicken/egg:
  *   the plugin doesn't have permission to allocate Stata memory (in this case matrices),
  *   but we don't know how much to allocate before interrogating the svm_model

  local have_sv_indices = 0
  local have_rho = 0
  local have_probA = 0 /*an undefined macro will inconsistently cause an eval error because `have_rho'==1 will eval to ==1 will eval to "unknown variable"*/
  local have_probB = 0 /*so just define them ahead of time to be safe*/

  * Phase 1
  plugin call _svm, "_model2stata" 1

  * the total number of (detected?) classes
  ereturn scalar nr_class = _model2stata_nr_class
  scalar drop _model2stata_nr_class

  * the number of support vectors
  ereturn scalar l = _model2stata_l
  scalar drop _model2stata_l

  * Phase 2
  * Allocate Stata matrices and copy the libsvm matrices and vectors

  *TODO: SV and sv_indices are probably best translated jointly by adding an indicator variable column to the original dataset telling if that observation was chosen as a support vector (of course, this will mean we need to further clamp the upper limit)

  if(`have_sv_indices'==1 & `e(l)'>0) {
    matrix SVs = J(e(l),1,.) /*but for now we can just copy out sv_indices*/
    matrix colnames SVs = "SVs"
  }

  * nSV tells how many support vectors went into each class
  * The sum of the entries in nSV should be l
  matrix nSV = J(e(nr_class),1,.)
  matrix colnames nSV = "nSV"
  /*matrix rownames nSV = . . */

  if(`e(nr_class)'>0) {
    matrix labels = J(e(nr_class),1,.)
    matrix colnames labels = "labels"
  }

  if(`e(nr_class)'>1 & `e(l)'>0) {
    matrix sv_coef = J(e(nr_class)-1,e(l),.)
  }

  if(`have_rho'==1 & `e(nr_class)'>0) {
    matrix rho = J(e(nr_class),e(nr_class),.)
  }
  if(`have_probA'==1 & `e(nr_class)'>0) {
    matrix probA = J(e(nr_class),e(nr_class),.)
  }
  if(`have_probB'==1 & `e(nr_class)'>0) {
    matrix probB = J(e(nr_class),e(nr_class),.)
  }

  * TODO: also label the rows according to model->label (libsvm's "labels" are just more integers, but it helps to be consistent anyway);
  *  I can easily extract ->label with the same code, but attaching it to the rownames of the other is tricky
  plugin call _svm, "_model2stata" 2

  * Phase 3
  * Export the rest of the values to e()
  * We *cannot* export matrices to e() from the C interface, hence we have to do this very explicit thing
  * NOTE: 'ereturn matrix' erases the old name (unless you specify ,copy), which is why we don't have to explicitly drop things
  *       'ereturn scalar' doesn't do this, because Stata loves being consistent. Just go read the docs for 'syntax' and see how easy it is.
  * All of these are silenced because various things might kill any of them, and we want failures to be independent of each other

  quietly capture ereturn matrix SVs = SVs

  quietly capture ereturn matrix nSV = nSV
  quietly capture ereturn matrix labels = labels

  quietly capture ereturn matrix sv_coef = sv_coef
  quietly capture ereturn matrix rho = rho
  //quietly capture ereturn matrix probA = probA //XXX disabled: these are probably not something you care to look at directly
  //quietly capture ereturn matrix probB = probB //              rather, use "predict, prob"; if a compelling reason to expose these comes up we can

end


redo: don't use ccache

redo: don't use ccache

#!/bin/bash
# automaton
# (c) 2013 Justin Gottula
# The source code of this project is distributed under the terms of the
# simplified BSD license. See the LICENSE file for details.

exec >&2

TARGET=$1
TARGET_BASE=$2
OUTPUT=$3

CC="avr-gcc"
AS="avr-as"
LD="avr-ld"
NM="avr-nm"
OBJCOPY="avr-objcopy"
OBJDUMP="avr-objdump"

PROJ_NAME="automaton"

MCU="atmega324a"
CPU_KHZ="20000"

DEFINES=("F_CPU=${CPU_KHZ}000UL")
INC_DIRS=(src)
LIB_DIRS=()
LIBS=()

CFLAGS_CPU=("-mmcu=$MCU" -fpack-struct -ffreestanding)
CFLAGS_DEBUG=(-ggdb)
CFLAGS_OPT=(-Os -flto -fuse-linker-plugin -fwhole-program)
CFLAGS_WARN=(-Wall -Wextra -Wno-unused-function)
CFLAGS_ETC=(-fno-diagnostics-show-caret)

CFLAGS="-std=gnu11 ${CFLAGS_CPU[@]} ${CFLAGS_DEBUG[@]} ${CFLAGS_OPT[@]} \
${CFLAGS_WARN[@]} ${CFLAGS_ETC[@]}"
CFLAGS_COMPILE="$CFLAGS ${DEFINES[@]/#/-D} ${INC_DIRS[@]/#/-I}"
CFLAGS_LINK="$CFLAGS ${LIB_DIRS[@]/#/-L} ${LIBS[@]/#/-l}"

SOURCES=$(find src -type f -iname '*.c')
OBJECTS=${SOURCES//.c/.o}

OUT_ELF="out/$PROJ_NAME.elf"
OUT_BIN="out/$PROJ_NAME.bin"
OUT_HEX="out/$PROJ_NAME.hex"
OUT_MAP="out/$PROJ_NAME.map"
OUT_DUMP="out/$PROJ_NAME.dump"

case "$TARGET" in
all)
	redo-ifchange $OUT_ELF $OUT_BIN $OUT_HEX $OUT_MAP $OUT_DUMP
	;;
$OUT_ELF)
	redo-ifchange $OBJECTS
	$CC $CFLAGS_LINK -o $OUTPUT $OBJECTS
	;;
$OUT_BIN)
	redo-ifchange $OUT_ELF
	$OBJCOPY -O binary $OUT_ELF $OUTPUT
	;;
$OUT_HEX)
	redo-ifchange $OUT_ELF
	$OBJCOPY -O ihex $OUT_ELF $OUTPUT
	;;
$OUT_MAP)
	redo-ifchange $OUT_ELF
	$NM -Cn $OUT_ELF >$OUTPUT
	;;
$OUT_DUMP)
	redo-ifchange $OUT_ELF
	$OBJDUMP -aCdfhpS $OUT_ELF >$OUTPUT
	;;
*.o)
	$CC $CFLAGS_COMPILE -o${TARGET//.o/.dep} -MM -MG ${TARGET//.o/.c}

	read DEPS <${TARGET//.o/.dep}
	redo-ifchange ${DEPS#*:}

	$CC $CFLAGS_COMPILE -o$OUTPUT -c ${TARGET//.o/.c}
	;;
clean)
	rm -rf $(find out -type f)
	rm -rf $(find src -type f -iname *.o)
	rm -rf $(find src -type f -iname *.dep)
	;;
load)
	redo-ifchange $OUT_HEX

	AVRDUDE_CMD="sudo avrdude -u -p ${MCU/atmega/m} -c usbasp -B 460800 \
	-U flash:w:$OUT_HEX"

	# retry once upon failure
	$($AVRDUDE_CMD)
	if [[ "$?" != "0" ]]; then
		$($AVRDUDE_CMD)
	fi
	;;
*)
	echo "unknown target '$TARGET'"
	exit 1
	;;
esac

#!/bin/bash
# automaton
# (c) 2013 Justin Gottula
# The source code of this project is distributed under the terms of the
# simplified BSD license. See the LICENSE file for details.

exec >&2

TARGET=$1
TARGET_BASE=$2
OUTPUT=$3

CC="ccache avr-gcc"
AS="avr-as"
LD="avr-ld"
NM="avr-nm"
OBJCOPY="avr-objcopy"
OBJDUMP="avr-objdump"

PROJ_NAME="automaton"

MCU="atmega324a"
CPU_KHZ="20000"

DEFINES=("F_CPU=${CPU_KHZ}000UL")
INC_DIRS=(src)
LIB_DIRS=()
LIBS=()

CFLAGS_CPU=("-mmcu=$MCU" -fpack-struct -ffreestanding)
CFLAGS_DEBUG=(-ggdb)
CFLAGS_OPT=(-Os -flto -fuse-linker-plugin -fwhole-program)
CFLAGS_WARN=(-Wall -Wextra -Wno-unused-function)
CFLAGS_ETC=(-fno-diagnostics-show-caret)

CFLAGS="-std=gnu11 ${CFLAGS_CPU[@]} ${CFLAGS_DEBUG[@]} ${CFLAGS_OPT[@]} \
${CFLAGS_WARN[@]} ${CFLAGS_ETC[@]}"
CFLAGS_COMPILE="$CFLAGS ${DEFINES[@]/#/-D} ${INC_DIRS[@]/#/-I}"
CFLAGS_LINK="$CFLAGS ${LIB_DIRS[@]/#/-L} ${LIBS[@]/#/-l}"

SOURCES=$(find src -type f -iname '*.c')
OBJECTS=${SOURCES//.c/.o}

OUT_ELF="out/$PROJ_NAME.elf"
OUT_BIN="out/$PROJ_NAME.bin"
OUT_HEX="out/$PROJ_NAME.hex"
OUT_MAP="out/$PROJ_NAME.map"
OUT_DUMP="out/$PROJ_NAME.dump"

case "$TARGET" in
all)
	redo-ifchange $OUT_ELF $OUT_BIN $OUT_HEX $OUT_MAP $OUT_DUMP
	;;
$OUT_ELF)
	redo-ifchange $OBJECTS
	$CC $CFLAGS_LINK -o $OUTPUT $OBJECTS
	;;
$OUT_BIN)
	redo-ifchange $OUT_ELF
	$OBJCOPY -O binary $OUT_ELF $OUTPUT
	;;
$OUT_HEX)
	redo-ifchange $OUT_ELF
	$OBJCOPY -O ihex $OUT_ELF $OUTPUT
	;;
$OUT_MAP)
	redo-ifchange $OUT_ELF
	$NM -Cn $OUT_ELF >$OUTPUT
	;;
$OUT_DUMP)
	redo-ifchange $OUT_ELF
	$OBJDUMP -aCdfhpS $OUT_ELF >$OUTPUT
	;;
*.o)
	$CC $CFLAGS_COMPILE -o${TARGET//.o/.dep} -MM -MG ${TARGET//.o/.c}

	read DEPS <${TARGET//.o/.dep}
	redo-ifchange ${DEPS#*:}

	$CC $CFLAGS_COMPILE -o$OUTPUT -c ${TARGET//.o/.c}
	;;
clean)
	rm -rf $(find out -type f)
	rm -rf $(find src -type f -iname *.o)
	rm -rf $(find src -type f -iname *.dep)
	;;
load)
	redo-ifchange $OUT_HEX

	AVRDUDE_CMD="sudo avrdude -u -p ${MCU/atmega/m} -c usbasp -B 460800 \
	-U flash:w:$OUT_HEX"

	# retry once upon failure
	$($AVRDUDE_CMD)
	if [[ "$?" != "0" ]]; then
		$($AVRDUDE_CMD)
	fi
	;;
*)
	echo "unknown target '$TARGET'"
	exit 1
	;;
esac