1 //   OpenNN: Open Neural Networks Library
2 //   www.opennn.net
3 //
4 //   B O U N D I N G   L A Y E R   C L A S S
5 //
6 //   Artificial Intelligence Techniques SL
7 //   artelnics@artelnics.com
8 
9 #include "bounding_layer.h"
10 
11 namespace OpenNN
12 {
13 
14 /// Default constructor.
15 /// It creates a bounding layer object with zero bounding neurons.
16 
BoundingLayer()17 BoundingLayer::BoundingLayer() : Layer()
18 {
19     set();
20 
21     set_default();
22 }
23 
24 
25 /// Bounding neurons number constructor.
26 /// It creates a bounding layer with a given size.
27 /// @param neurons_number Number of bounding neurons in the layer.
28 
BoundingLayer(const Index & neurons_number)29 BoundingLayer::BoundingLayer(const Index& neurons_number) : Layer()
30 {
31     set(neurons_number);
32 
33     set_default();
34 }
35 
36 
37 /// Destructor.
38 /// This destructor does not delete any pointer.
39 
~BoundingLayer()40 BoundingLayer::~BoundingLayer()
41 {
42 }
43 
44 
45 /// Returns true if the size of the layer is zero, and false otherwise.
46 
is_empty() const47 bool BoundingLayer::is_empty() const
48 {
49     if(get_neurons_number() == 0)
50     {
51         return true;
52     }
53     else
54     {
55         return false;
56     }
57 }
58 
59 
60 /// Returns the method used for bounding layer.
61 
get_bounding_method() const62 const BoundingLayer::BoundingMethod& BoundingLayer::get_bounding_method() const
63 {
64     return bounding_method;
65 }
66 
67 /// Returns a string writing if use bounding layer or not.
68 
write_bounding_method() const69 string BoundingLayer::write_bounding_method() const
70 {
71     if(bounding_method == Bounding)
72     {
73         return "Bounding";
74     }
75     else if(bounding_method == NoBounding)
76     {
77         return "NoBounding";
78     }
79     else
80     {
81         ostringstream buffer;
82 
83         buffer << "OpenNN Exception: BoundingLayer class.\n"
84                << "string write_bounding_method() const method.\n"
85                << "Unknown bounding method.\n";
86 
87         throw logic_error(buffer.str());
88     }
89 }
90 
91 
92 /// Get number of inputs
93 
get_inputs_number() const94 Index BoundingLayer::get_inputs_number() const
95 {
96     return lower_bounds.dimension(0);
97 }
98 
99 
100 /// Return the neurons number in the bounding layer.
101 
get_neurons_number() const102 Index BoundingLayer::get_neurons_number() const
103 {
104     return lower_bounds.dimension(0);
105 }
106 
107 
108 /// Returns the lower bounds values of all the bounding neurons in the layer.
109 
get_lower_bounds() const110 const Tensor<type, 1>& BoundingLayer::get_lower_bounds() const
111 {
112     return lower_bounds;
113 }
114 
115 
116 /// Returns the lower bound value of a single bounding neuron.
117 /// @param i Index of bounding neuron.
118 
get_lower_bound(const Index & i) const119 type BoundingLayer::get_lower_bound(const Index& i) const
120 {
121 
122 
123 #ifdef __OPENNN_DEBUG__
124 
125     const Index neurons_number = get_neurons_number();
126 
127     if(i >= neurons_number)
128     {
129         ostringstream buffer;
130 
131         buffer << "OpenNN Exception: BoundingLayer class.\n"
132                << "type get_lower_bound(const Index&) const method.\n"
133                << "Index must be less than number of bounding neurons.\n";
134 
135         throw logic_error(buffer.str());
136     }
137 
138 #endif
139 
140     return lower_bounds[i];
141 }
142 
143 
144 /// Returns the upper bounds values of all the bounding neurons in the layer.
145 
get_upper_bounds() const146 const Tensor<type, 1>& BoundingLayer::get_upper_bounds() const
147 {
148     return upper_bounds;
149 }
150 
151 
152 /// Returns the upper bound value of a single bounding neuron.
153 /// @param i Index of bounding neuron.
154 
get_upper_bound(const Index & i) const155 type BoundingLayer::get_upper_bound(const Index& i) const
156 {
157 #ifdef __OPENNN_DEBUG__
158 
159     const Index neurons_number = get_neurons_number();
160 
161     if(neurons_number == 0)
162     {
163         ostringstream buffer;
164 
165         buffer << "OpenNN Exception: BoundingLayer class.\n"
166                << "type get_upper_bound(const Index&) const method.\n"
167                << "Number of bounding neurons is zero.\n";
168 
169         throw logic_error(buffer.str());
170     }
171     else if(i >= neurons_number)
172     {
173         ostringstream buffer;
174 
175         buffer << "OpenNN Exception: BoundingLayer class.\n"
176                << "type get_upper_bound(const Index&) const method.\n"
177                << "Index must be less than number of bounding neurons.\n";
178 
179         throw logic_error(buffer.str());
180     }
181 
182 #endif
183 
184     return upper_bounds(i);
185 }
186 
187 
188 /// Sets the number of bounding neurons to be zero.
189 /// It also sets the rest of memebers to their default values.
190 
set()191 void BoundingLayer::set()
192 {
193     bounding_method = Bounding;
194 
195     lower_bounds.resize(0);
196     upper_bounds.resize(0);
197 
198     set_default();
199 }
200 
201 
202 /// Resizes the bounding layer.
203 /// It also sets the rest of memebers to their default values.
204 /// @param new_neurons_number Size of the bounding layer.
205 
set(const Index & new_neurons_number)206 void BoundingLayer::set(const Index& new_neurons_number)
207 {
208     set_neurons_number(new_neurons_number);
209 
210     set_default();
211 }
212 
213 
214 /// Resize the number of inputs.
215 /// @param new_inputs_number Size of the inputs array.
216 
set_inputs_number(const Index & new_inputs_number)217 void BoundingLayer::set_inputs_number(const Index& new_inputs_number)
218 {
219     lower_bounds.resize(new_inputs_number);
220     upper_bounds.resize(new_inputs_number);
221 }
222 
223 
224 /// Resize the number of bound neurons from the bounding layer.
225 /// @param new_neurons_number Number of the neurons from the bounding layer.
226 
set_neurons_number(const Index & new_neurons_number)227 void BoundingLayer::set_neurons_number(const Index& new_neurons_number)
228 {
229     lower_bounds.resize(new_neurons_number);
230     upper_bounds.resize(new_neurons_number);
231 
232     lower_bounds.setConstant(-numeric_limits<type>::max());
233     upper_bounds.setConstant(numeric_limits<type>::max());
234 }
235 
236 
237 /// Sets the bounding layer members from a XML document.
238 /// @param bounding_layer_document Pointer to a TinyXML document containing the member data.
239 
set(const tinyxml2::XMLDocument & bounding_layer_document)240 void BoundingLayer::set(const tinyxml2::XMLDocument& bounding_layer_document)
241 {
242     set_default();
243 
244     from_XML(bounding_layer_document);
245 }
246 
247 
248 /// Sets the members of this object to be the members of another object of the same class.
249 /// @param other_bounding_layer Object to be copied.
250 
set(const BoundingLayer & other_bounding_layer)251 void BoundingLayer::set(const BoundingLayer& other_bounding_layer)
252 {
253     lower_bounds = other_bounding_layer.lower_bounds;
254 
255     upper_bounds = other_bounding_layer.upper_bounds;
256 
257     display = other_bounding_layer.display;
258 }
259 
260 
261 /// Sets a new bounding method.
262 /// @param new_method New bounding method.
263 
set_bounding_method(const BoundingMethod & new_method)264 void BoundingLayer::set_bounding_method(const BoundingMethod& new_method)
265 {
266     bounding_method = new_method;
267 }
268 
269 
270 /// Sets a new bounding method.
271 /// @param new_method_string New bounding method string.
272 
set_bounding_method(const string & new_method_string)273 void BoundingLayer::set_bounding_method(const string& new_method_string)
274 {
275     if(new_method_string == "NoBounding")
276     {
277         bounding_method = NoBounding;
278     }
279     else if(new_method_string == "Bounding")
280     {
281         bounding_method = Bounding;
282     }
283     else
284     {
285         ostringstream buffer;
286 
287         buffer << "OpenNN Exception: BoundingLayer class.\n"
288                << "void set_bounding_method(const string&) method.\n"
289                << "Unknown bounding method: " << new_method_string << ".\n";
290 
291         throw logic_error(buffer.str());
292     }
293 }
294 
295 
296 /// Sets new lower bounds for all the neurons in the layer.
297 /// @param new_lower_bounds New set of lower bounds for the bounding neurons.
298 
set_lower_bounds(const Tensor<type,1> & new_lower_bounds)299 void BoundingLayer::set_lower_bounds(const Tensor<type, 1>& new_lower_bounds)
300 {
301 #ifdef __OPENNN_DEBUG__
302 
303     const Index neurons_number = get_neurons_number();
304 
305     if(new_lower_bounds.size() != neurons_number)
306     {
307         ostringstream buffer;
308 
309         buffer << "OpenNN Exception: BoundingLayer class.\n"
310                << "void set_lower_bounds(const Tensor<type, 1>&) method.\n"
311                << "Size must be equal to number of bounding neurons number.\n";
312 
313         throw logic_error(buffer.str());
314     }
315 
316 #endif
317 
318     // Set lower bound of bounding neurons
319 
320     lower_bounds = new_lower_bounds;
321 }
322 
323 
324 /// Sets a new lower bound for a single neuron.
325 /// This value is used for unscaling that variable so that it is not less than the lower bound.
326 /// @param index Index of bounding neuron.
327 /// @param new_lower_bound New lower bound for the neuron with index i.
328 
set_lower_bound(const Index & index,const type & new_lower_bound)329 void BoundingLayer::set_lower_bound(const Index& index, const type& new_lower_bound)
330 {
331     const Index neurons_number = get_neurons_number();
332 
333 #ifdef __OPENNN_DEBUG__
334 
335     if(index >= neurons_number)
336     {
337         ostringstream buffer;
338 
339         buffer << "OpenNN Exception: BoundingLayer class.\n"
340                << "void set_lower_bound(const Index&, const type&) method.\n"
341                << "Index of bounding neurons must be less than number of bounding neurons.\n";
342 
343         throw logic_error(buffer.str());
344     }
345 
346 #endif
347 
348     if(lower_bounds.size() != neurons_number)
349     {
350         lower_bounds.resize(neurons_number);
351         lower_bounds.setConstant(-numeric_limits<type>::max());
352     }
353 
354     // Set lower bound of single neuron
355 
356     lower_bounds[index] = new_lower_bound;
357 }
358 
359 
360 /// Sets new upper bounds for all the bounding neurons.
361 /// These values are used for unscaling variables so that they are not greater than the upper bounds.
362 /// @param new_upper_bounds New set of upper bounds for the layer.
363 
set_upper_bounds(const Tensor<type,1> & new_upper_bounds)364 void BoundingLayer::set_upper_bounds(const Tensor<type, 1>& new_upper_bounds)
365 {
366 
367 
368 #ifdef __OPENNN_DEBUG__
369 
370     const Index neurons_number = get_neurons_number();
371 
372     if(new_upper_bounds.size() != neurons_number)
373     {
374         ostringstream buffer;
375 
376         buffer << "OpenNN Exception: BoundingLayer class.\n"
377                << "void set_upper_bound(const Tensor<type, 1>&) method.\n"
378                << "Size must be equal to number of bounding neurons.\n";
379 
380         throw logic_error(buffer.str());
381     }
382 
383 #endif
384 
385     // Set upper bound of neurons
386 
387     upper_bounds = new_upper_bounds;
388 }
389 
390 
391 /// Sets a new upper bound for a single neuron.
392 /// This value is used for unscaling that variable so that it is not greater than the upper bound.
393 /// @param index Index of bounding neuron.
394 /// @param new_upper_bound New upper bound for the bounding neuron with that index.
395 
set_upper_bound(const Index & index,const type & new_upper_bound)396 void BoundingLayer::set_upper_bound(const Index& index, const type& new_upper_bound)
397 {
398     const Index neurons_number = get_neurons_number();
399 
400 
401 
402 #ifdef __OPENNN_DEBUG__
403 
404     if(index >= neurons_number)
405     {
406         ostringstream buffer;
407 
408         buffer << "OpenNN Exception: BoundingLayer class.\n"
409                << "void set_upper_bound(const Index&, const type&) method.\n"
410                << "Index of bounding neuron must be less than number of bounding neurons.\n";
411 
412         throw logic_error(buffer.str());
413     }
414 
415 #endif
416 
417     if(upper_bounds.size() != neurons_number)
418     {
419         upper_bounds.resize(neurons_number);
420         upper_bounds.setConstant(numeric_limits<type>::max());
421     }
422 
423     upper_bounds[index] = new_upper_bound;
424 
425 }
426 
427 
428 /// Sets a new display value.
429 /// If it is set to true messages from this class are to be displayed on the screen;
430 /// if it is set to false messages from this class are not to be displayed on the screen.
431 /// @param new_display Display value.
432 
set_display(const bool & new_display)433 void BoundingLayer::set_display(const bool& new_display)
434 {
435     display = new_display;
436 }
437 
438 
439 /// Sets the members to their default values:
440 /// <ul>
441 /// <li> Display: True.
442 /// </ul>
443 
set_default()444 void BoundingLayer::set_default()
445 {
446     layer_name = "bounding_layer";
447 
448     bounding_method = Bounding;
449 
450     layer_type = Layer::Bounding;
451 }
452 
453 
454 /// Calculates the outputs from the bounding layer for a set of inputs to that layer.
455 /// @param inputs Set of inputs to the bounding layer.
456 
calculate_outputs(const Tensor<type,2> & inputs)457 Tensor<type, 2> BoundingLayer::calculate_outputs(const Tensor<type, 2>& inputs)
458 {
459 #ifdef __OPENNN_DEBUG__
460 
461     const Index inputs_dimensions_number = inputs.rank();
462 
463     if(inputs_dimensions_number != 2)
464     {
465         ostringstream buffer;
466 
467         buffer << "OpenNN Exception: BoundingLayer class.\n"
468                << "Tensor<type, 2> calculate_outputs(const Tensor<type, 2>&) const method.\n"
469                << "Number of dimensions (" << inputs_dimensions_number << ") must be 2.\n";
470 
471         throw logic_error(buffer.str());
472     }
473 
474     const Index inputs_number = get_inputs_number();
475 
476     const Index inputs_columns_number = inputs.dimension(1);
477 
478     if(inputs_columns_number != inputs_number)
479     {
480         ostringstream buffer;
481 
482         buffer << "OpenNN Exception: BoundingLayer class.\n"
483                << "Tensor<type, 2> calculate_outputs(const Tensor<type, 2>&) const method.\n"
484                << "Number of columns ("
485                << inputs_columns_number << ") must be equal to number of inputs ("
486                << inputs_number << ").\n";
487 
488         throw logic_error(buffer.str());
489     }
490 
491 #endif
492 
493     switch(bounding_method)
494     {
495         case NoBounding: return inputs;
496 
497         case Bounding:
498         {
499             const Index rows_number = inputs.dimension(0);
500             const Index columns_number = inputs.dimension(1);
501 
502             Tensor<type, 2> outputs(rows_number, columns_number);
503 
504             for(Index i = 0; i < rows_number; i++)
505             {
506                 for(Index j = 0; j < columns_number; j++)
507                 {
508                     if(inputs(i,j) < lower_bounds(j)) outputs(i,j) = lower_bounds(j);
509                     else if(inputs(i,j) > upper_bounds(j)) outputs(i,j) = upper_bounds(j);
510                     else outputs(i,j) = inputs(i,j);
511                 }
512             }
513 
514             return outputs;
515         }
516     }
517 
518     return Tensor<type, 2>();
519 }
520 
521 
522 /// Returns a string with the expression of the lower and upper bounds functions.
523 
write_expression(const Tensor<string,1> & inputs_names,const Tensor<string,1> & outputs_names) const524 string BoundingLayer::write_expression(const Tensor<string, 1>& inputs_names, const Tensor<string, 1>& outputs_names) const
525 {
526     ostringstream buffer;
527 
528     buffer.precision(10);
529 
530     if(bounding_method == Bounding)
531     {
532         const Index neurons_number = get_neurons_number();
533 
534         for(Index i = 0; i < neurons_number; i++)
535         {
536             buffer << outputs_names[i] << " = max(" << lower_bounds[i] << ", " << inputs_names[i] << ")\n";
537             buffer << outputs_names[i] << " = min(" << upper_bounds[i] << ", " << inputs_names[i] << ")\n";
538         }
539     }
540     else
541     {
542         buffer << "";
543     }
544 
545     return buffer.str();
546 }
547 
548 
549 ///
550 /// \brief BoundingLayer::write_expression_c
551 /// \return
552 
write_expression_c() const553 string BoundingLayer::write_expression_c() const
554 {
555     const Index neurons_number = get_neurons_number();
556 
557     ostringstream buffer;
558 
559     buffer << "vector<float> " << layer_name << "(const vector<float>& inputs)\n{" << endl;
560 
561     buffer << "\tvector<float> outputs(" << neurons_number << ");\n" << endl;
562 
563     for(Index i = 0; i < neurons_number; i++)
564     {
565         buffer << "\toutputs[" << i << "] = inputs[" << i << "];" << endl;
566     }
567 
568     buffer << "\n\treturn outputs;\n}" << endl;
569 
570     return buffer.str();
571 }
572 
573 
574 ///
575 /// \brief BoundingLayer::write_expression_python
576 /// \return
577 
write_expression_python() const578 string BoundingLayer::write_expression_python() const
579 {
580     const Index neurons_number = get_neurons_number();
581 
582     ostringstream buffer;
583 
584     buffer << "def " << layer_name << "(inputs):\n" << endl;
585 
586     buffer << "\toutputs = [None] * "<<neurons_number<<"\n" << endl;
587 
588     for(Index i = 0; i < neurons_number; i++)
589     {
590         buffer << "\toutputs[" << i << "] = inputs[" << i << "]" << endl;
591     }
592 
593     buffer << "\n\treturn outputs\n" << endl;
594 
595     return buffer.str();
596 }
597 
598 
599 /// Serializes the bounding layer object into a XML document of the TinyXML library without keep the DOM tree in memory.
600 /// See the OpenNN manual for more information about the format of this document.
601 
write_XML(tinyxml2::XMLPrinter & file_stream) const602 void BoundingLayer::write_XML(tinyxml2::XMLPrinter& file_stream) const
603 {
604     ostringstream buffer;
605 
606     file_stream.OpenElement("BoundingLayer");
607 
608     // Bounding neurons number
609 
610     file_stream.OpenElement("BoundingNeuronsNumber");
611 
612     const Index neurons_number = get_neurons_number();
613 
614     buffer.str("");
615     buffer << neurons_number;
616 
617     file_stream.PushText(buffer.str().c_str());
618 
619     file_stream.CloseElement();
620 
621     for(Index i = 0; i < neurons_number; i++)
622     {
623         file_stream.OpenElement("Item");
624 
625         file_stream.PushAttribute("Index",static_cast<unsigned>(i+1));
626 
627         // Lower bound
628 
629         file_stream.OpenElement("LowerBound");
630 
631         buffer.str("");
632         buffer << lower_bounds[i];
633 
634         file_stream.PushText(buffer.str().c_str());
635 
636         file_stream.CloseElement();
637 
638         // Upper bound
639 
640         file_stream.OpenElement("UpperBound");
641 
642         buffer.str("");
643         buffer << upper_bounds[i];
644 
645         file_stream.PushText(buffer.str().c_str());
646 
647         file_stream.CloseElement();
648 
649 
650         file_stream.CloseElement();
651     }
652 
653     // Bounding method
654 
655     file_stream.OpenElement("UseBoundingLayer");
656 
657     if(bounding_method == Bounding)
658     {
659         buffer.str("");
660         buffer << 1;
661     }
662     else if(bounding_method == NoBounding)
663     {
664         buffer.str("");
665         buffer << 0;
666     }
667     else
668     {
669         file_stream.CloseElement();
670 
671         buffer << "OpenNN Exception: BoundingLayer class.\n"
672                << "void write_XML(tinyxml2::XMLPrinter&) const method.\n"
673                << "Unknown bounding method type.\n";
674 
675         throw logic_error(buffer.str());
676     }
677 
678     file_stream.PushText(buffer.str().c_str());
679 
680     file_stream.CloseElement();
681 
682 //   // Display
683 
684 //   {
685 //      file_stream.OpenElement("Display");
686 
687 //      buffer.str("");
688 //      buffer << display;
689 
690 //      file_stream.PushText(buffer.str().c_str());
691 
692 //      file_stream.CloseElement();
693 //   }
694 
695     file_stream.CloseElement();
696 }
697 
698 
699 /// Deserializes a TinyXML document into this bounding layer object.
700 /// @param document TinyXML document containing the member data.
701 
from_XML(const tinyxml2::XMLDocument & document)702 void BoundingLayer::from_XML(const tinyxml2::XMLDocument& document)
703 {
704     ostringstream buffer;
705 
706     const tinyxml2::XMLElement* bounding_layer_element = document.FirstChildElement("BoundingLayer");
707 
708     if(!bounding_layer_element)
709     {
710         buffer << "OpenNN Exception: BoundingLayer class.\n"
711                << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
712                << "BoundingLayer element is nullptr.\n";
713 
714         throw logic_error(buffer.str());
715     }
716 
717     // Bounding neurons number
718 
719     const tinyxml2::XMLElement* neurons_number_element = bounding_layer_element->FirstChildElement("BoundingNeuronsNumber");
720 
721     if(!neurons_number_element)
722     {
723         buffer << "OpenNN Exception: BoundingLayer class.\n"
724                << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
725                << "BoundingNeuronsNumber element is nullptr.\n";
726 
727         throw logic_error(buffer.str());
728     }
729 
730     const Index neurons_number = static_cast<Index>(atoi(neurons_number_element->GetText()));
731 
732     set(neurons_number);
733 
734     unsigned index = 0; // Index does not work
735 
736     if(neurons_number > 0)
737     {
738         const tinyxml2::XMLElement* start_element = neurons_number_element;
739 
740         for(Index i = 0; i < lower_bounds.size(); i++)
741         {
742             const tinyxml2::XMLElement* item_element = start_element->NextSiblingElement("Item");
743             start_element = item_element;
744 
745             if(!item_element)
746             {
747                 buffer << "OpenNN Exception: BoundingLayer class.\n"
748                        << "void from_XML(const tinyxml2::XMLElement*) method.\n"
749                        << "Item " << i+1 << " is nullptr.\n";
750 
751                 throw logic_error(buffer.str());
752             }
753 
754             item_element->QueryUnsignedAttribute("Index", &index);
755 
756             if(index != i+1)
757             {
758                 buffer << "OpenNN Exception: BoundingLayer class.\n"
759                        << "void from_XML(const tinyxml2::XMLElement*) method.\n"
760                        << "Index " << index << " is not correct.\n";
761 
762                 throw logic_error(buffer.str());
763             }
764 
765             // Lower bound
766 
767             const tinyxml2::XMLElement* lower_bound_element = item_element->FirstChildElement("LowerBound");
768 
769             if(lower_bound_element)
770             {
771                 if(lower_bound_element->GetText())
772                 {
773                     lower_bounds[index-1] = static_cast<type>(atof(lower_bound_element->GetText()));
774                 }
775             }
776 
777             // Upper bound
778 
779             const tinyxml2::XMLElement* upper_bound_element = item_element->FirstChildElement("UpperBound");
780 
781             if(upper_bound_element)
782             {
783                 if(upper_bound_element->GetText())
784                 {
785                     upper_bounds[index-1] = static_cast<type>(atof(upper_bound_element->GetText()));
786                 }
787             }
788         }
789     }
790 
791     // Use bounding layer
792     {
793         const tinyxml2::XMLElement* use_bounding_layer_element = bounding_layer_element->FirstChildElement("UseBoundingLayer");
794 
795         if(use_bounding_layer_element)
796         {
797             Index new_method = static_cast<Index>(atoi(use_bounding_layer_element->GetText()));
798 
799             if(new_method == 1)
800             {
801                 bounding_method = Bounding;
802             }
803             else if(new_method == 0)
804             {
805                 bounding_method = NoBounding;
806             }
807             else
808             {
809                 buffer << "OpenNN Exception: BoundingLayer class.\n"
810                        << "void from_XML(const tinyxml2::XMLElement*) method.\n"
811                        << "Unknown bounding method.\n";
812 
813                 throw logic_error(buffer.str());
814             }
815         }
816     }
817 }
818 
819 }
820 
821 // OpenNN: Open Neural Networks Library.
822 // Copyright(C) 2005-2020 Artificial Intelligence Techniques, SL.
823 //
824 // This library is free software; you can redistribute it and/or
825 // modify it under the terms of the GNU Lesser General Public
826 // License as published by the Free Software Foundation; either
827 // version 2.1 of the License, or any later version.
828 //
829 // This library is distributed in the hope that it will be useful,
830 // but WITHOUT ANY WARRANTY; without even the implied warranty of
831 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
832 // Lesser General Public License for more details.
833 
834 // You should have received a copy of the GNU Lesser General Public
835 // License along with this library; if not, write to the Free Software
836 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
837