    // Copyright (C) 2007  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_MLP_KERNEl_C_
    #define DLIB_MLP_KERNEl_C_

    #include "mlp_kernel_abstract.h"
    #include "../algs.h"
    #include "../assert.h"

    namespace dlib
    {

        template <
            typename mlp_base // is an implementation of mlp_kernel_abstract.h
            >
        class mlp_kernel_c : public mlp_base
        {
            long verify_constructor_args (
                long nodes_in_input_layer,
                long nodes_in_first_hidden_layer,
                long nodes_in_second_hidden_layer,
                long nodes_in_output_layer
                )
            {
                // make sure requires clause is not broken
                DLIB_CASSERT(nodes_in_input_layer > 0 &&
                        nodes_in_first_hidden_layer > 0 &&
                        nodes_in_second_hidden_layer >= 0 &&
                        nodes_in_output_layer > 0,
                        "\tconst mlp::constructor()"
                        << "\n\tinvalid constructor arguments"
                        << "\n\tnodes_in_input_layer:         " << nodes_in_input_layer
                        << "\n\tnodes_in_first_hidden_layer:  " << nodes_in_first_hidden_layer
                        << "\n\tnodes_in_second_hidden_layer: " << nodes_in_second_hidden_layer
                        << "\n\tnodes_in_output_layer:        " << nodes_in_output_layer
                );

                return nodes_in_input_layer;
            }

        public:

            mlp_kernel_c (
                long nodes_in_input_layer,
                long nodes_in_first_hidden_layer,
                long nodes_in_second_hidden_layer = 0,
                long nodes_in_output_layer = 1,
                double alpha = 0.1,
                double momentum = 0.8
            ) : mlp_base( verify_constructor_args(
                            nodes_in_input_layer,
                            nodes_in_input_layer,
                            nodes_in_second_hidden_layer,
                            nodes_in_output_layer),
                          nodes_in_first_hidden_layer,
                          nodes_in_second_hidden_layer,
                          nodes_in_output_layer,
                          alpha,
                          momentum)
            {
            }

            template <typename EXP>
            const matrix<double> operator() (
                const matrix_exp<EXP>& in
            ) const
            {
                // make sure requires clause is not broken
                DLIB_CASSERT(in.nr() == this->input_layer_nodes() &&
                        in.nc() == 1,
                        "\tconst matrix<double> mlp::operator()(matrix_exp)"
                        << "\n\tthe input matrix dimensions are not correct"
                        << "\n\tin.nr():             " << in.nr()
                        << "\n\tin.nc():             " << in.nc()
                        << "\n\tinput_layer_nodes(): " << this->input_layer_nodes()
                        << "\n\tthis:                " << this
                );

                return mlp_base::operator()(in);
            }

            template <typename EXP1, typename EXP2>
            void train (
                const matrix_exp<EXP1>& example_in,
                const matrix_exp<EXP2>& example_out
            )
            {
                // make sure requires clause is not broken
                DLIB_CASSERT(example_in.nr() == this->input_layer_nodes() &&
                        example_in.nc() == 1 &&
                        example_out.nr() == this->output_layer_nodes() &&
                        example_out.nc() == 1 &&
                        max(example_out) <= 1.0 && min(example_out) >= 0.0,
                        "\tvoid mlp::train(matrix_exp, matrix_exp)"
                        << "\n\tthe training example dimensions are not correct"
                        << "\n\texample_in.nr():      " << example_in.nr()
                        << "\n\texample_in.nc():      " << example_in.nc()
                        << "\n\texample_out.nr():     " << example_out.nr()
                        << "\n\texample_out.nc():     " << example_out.nc()
                        << "\n\tmax(example_out):     " << max(example_out)
                        << "\n\tmin(example_out):     " << min(example_out)
                        << "\n\tinput_layer_nodes():  " << this->input_layer_nodes()
                        << "\n\toutput_layer_nodes(): " << this->output_layer_nodes()
                        << "\n\tthis:                 " << this
                );

                mlp_base::train(example_in,example_out);
            }

            template <typename EXP>
            void train (
                const matrix_exp<EXP>& example_in,
                double example_out
            )
            {
                // make sure requires clause is not broken
                DLIB_CASSERT(example_in.nr() == this->input_layer_nodes() &&
                        example_in.nc() == 1 &&
                        this->output_layer_nodes() == 1 &&
                        example_out <= 1.0 && example_out >= 0.0,
                        "\tvoid mlp::train(matrix_exp, double)"
                        << "\n\tthe training example dimensions are not correct"
                        << "\n\texample_in.nr():      " << example_in.nr()
                        << "\n\texample_in.nc():      " << example_in.nc()
                        << "\n\texample_out:          " << example_out
                        << "\n\tinput_layer_nodes():  " << this->input_layer_nodes()
                        << "\n\toutput_layer_nodes(): " << this->output_layer_nodes()
                        << "\n\tthis:                 " << this
                );

                mlp_base::train(example_in,example_out);
            }

        };

        template <
            typename mlp_base
            >
        inline void swap (
            mlp_kernel_c<mlp_base>& a,
            mlp_kernel_c<mlp_base>& b
        ) { a.swap(b); }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_MLP_KERNEl_C_