<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE document PUBLIC "-//CNX//DTD CNXML 0.5 plus MathML//EN" "http://cnx.rice.edu/cnxml/0.5/DTD/cnxml_mathml.dtd">
<document xmlns="http://cnx.rice.edu/cnxml" xmlns:md="http://cnx.rice.edu/mdml/0.4" xmlns:bib="http://bibtexml.sf.net/" xmlns:m="http://www.w3.org/1998/Math/MathML" id="new">
  <name>Signal Processing in Processing: Convolution and Filtering</name>
  <metadata>
  <md:version>1.2</md:version>
  <md:created>2005/10/03 03:07:40 GMT-5</md:created>
  <md:revised>2005/10/24 08:10:37.591 GMT-5</md:revised>
  <md:authorlist>
      <md:author id="drocchesso">
      <md:firstname>Davide</md:firstname>
      
      <md:surname>Rocchesso</md:surname>
      <md:email>Davide.Rocchesso@univr.it</md:email>
    </md:author>
      <md:author id="ppolotti">
      <md:firstname>Pietro</md:firstname>
      
      <md:surname>Polotti</md:surname>
      <md:email>polotti@sci.univr.it</md:email>
    </md:author>
  </md:authorlist>

  <md:maintainerlist>
    <md:maintainer id="drocchesso">
      <md:firstname>Davide</md:firstname>
      
      <md:surname>Rocchesso</md:surname>
      <md:email>Davide.Rocchesso@univr.it</md:email>
    </md:maintainer>
  </md:maintainerlist>
  
  <md:keywordlist>
    <md:keyword>Convolution</md:keyword>
    <md:keyword>Filtering</md:keyword>
    <md:keyword>Systems</md:keyword>
  </md:keywordlist>

  <md:abstract>Discrete-time systems and their description with the impulse response. The convolution operator. Filtering.</md:abstract>
</metadata>
  <content>
    <section id="systems">
      <name>Systems</name> <para id="systemsp">For our purposes, a
      system is any processing element that, given as input a sequence
      of samples <m:math>
	  <m:apply>
	      <m:ci type="fn">x</m:ci>
	      <m:ci>n</m:ci>
	  </m:apply>
	</m:math>, produces as output a sequence of samples <m:math>
	  <m:apply>
	      <m:ci type="fn">y</m:ci>
	      <m:ci>n</m:ci>
	  </m:apply>
	</m:math>. If the samples are coming from a temporal series we
	talk about <term>discrete-time systems</term>. In this module
	we will not be concerned with continuous-time processing, even
	though the principles here described can be generalized to
	functions of continuous variable. Instead, the sequence of
	number can come from the sampling of an image, and in this
	case it will be appropriate to talk of <term>discrete-space
	systems</term> and use two indeces <m:math> <m:ci>m</m:ci>
	</m:math> and <m:math> <m:ci>n</m:ci> </m:math> if sampling is
	done by a rectangular grid of rows and columns.  </para>

      <para id="linearsysp">In this module we are only dealing with 
      <term>linear systems</term>, thus meaning that the following principle holds:
      <definition id="superposition"> <term>Superposition principle</term> <meaning>If <m:math>
	      <m:msub>
	      <m:ci type="fn">y</m:ci>
		<m:mn>1</m:mn>
	      </m:msub>
	</m:math> and <m:math>
	      <m:msub>
	      <m:ci type="fn">y</m:ci>
		<m:mn>2</m:mn>
	      </m:msub>
	</m:math> are the responses to the input sequences <m:math>
	      <m:msub>
	      <m:ci type="fn">x</m:ci>
		<m:mn>1</m:mn>
	      </m:msub>
	</m:math> and <m:math>
	      <m:msub>
	      <m:ci type="fn">x</m:ci>
		<m:mn>2</m:mn>
	      </m:msub>
	  </m:math> then the input <m:math>
	      <m:apply>
		<m:plus/>
		<m:apply>
		  <m:times/>
		  <m:msub>
		    <m:ci>a</m:ci>
		    <m:cn>1</m:cn>
		  </m:msub>
		  <m:msub>
		    <m:ci>x</m:ci>
		    <m:cn>1</m:cn>
		  </m:msub>
		</m:apply>
		<m:apply>
		  <m:times/>
		  <m:msub>
		    <m:ci>a</m:ci>
		    <m:cn>2</m:cn>
		  </m:msub>
		  <m:msub>
		    <m:ci>x</m:ci>
		    <m:cn>2</m:cn>
		  </m:msub>
		</m:apply>
	      </m:apply>
	    </m:math>
produces the response
<m:math>
	      <m:apply>
		<m:plus/>
		<m:apply>
		  <m:times/>
		  <m:msub>
		    <m:ci>a</m:ci>
		    <m:cn>1</m:cn>
		  </m:msub>
		  <m:msub>
		    <m:ci>y</m:ci>
		    <m:cn>1</m:cn>
		  </m:msub>
		</m:apply>
		<m:apply>
		  <m:times/>
		  <m:msub>
		    <m:ci>a</m:ci>
		    <m:cn>2</m:cn>
		  </m:msub>
		  <m:msub>
		    <m:ci>y</m:ci>
		    <m:cn>2</m:cn>
		  </m:msub>
		</m:apply>
	      </m:apply>
	    </m:math>
	  </meaning>
	</definition>
</para>
      <para id="invariancep">Another important concept is time (and space) invariance.
	<definition id="invarianced">
	  <term>Time invariance</term> <meaning>A system is
	  time-invariant if a time shift of <m:math> <m:ci>D</m:ci>
	  </m:math> samples in the input results in the same time
	  shift in the output, i.e., <m:math> <m:apply> <m:ci type="fn">x</m:ci>
		<m:apply>
		  <m:minus/>
		  <m:ci>n</m:ci>
		  <m:ci>D</m:ci>
		</m:apply>
	      </m:apply>
	    </m:math> produces <m:math>	  <m:apply>
	      <m:ci type="fn">y</m:ci>
		<m:apply>
		  <m:minus/>
		  <m:ci>n</m:ci>
		  <m:ci>D</m:ci>
		</m:apply>
	      </m:apply>
	    </m:math>.
	  </meaning>
	</definition>
Cases of non-invariance are found whenever the system changes its
characteristics in time (or space), for example as an effect of human
control. Those systems where the sampling rate at the input is
different than the one at the output are also non-invariant. For
instance, decimators are time-variant systems.
</para>
      <para id="chain">A series connection of <term> linear
	time-invariant </term> (LTI) blocks is itself a linear and
	time-invariant system, and the order of blocks can be changed
	without affecting the input-output behavior. </para> <para id="lti">LTI systems can be thoroughly described by the
	response they give to a unit-magnitude impulse.</para>
      <definition id="impulsed">
	<term>The impulse in discrete time (space) </term>
	<meaning>is the signal <m:math>
	    <m:ci>δ</m:ci>
	  </m:math>
	  with value<m:math> <m:cn>1</m:cn> </m:math>
	  at the instant zero (in the point with coordinates <m:math>
	    <m:list>	    <m:cn>0</m:cn> 
	    <m:cn>0</m:cn> 
	    </m:list>
	  </m:math>), and <m:math> <m:cn>0</m:cn> </m:math> in any
	  other instant (point).
      </meaning>
      </definition>
    </section>
    <section id="convolution">
      <name>Impulse response and convolution</name> <para id="impulserespp">We call <m:math> <m:ci>h</m:ci> </m:math> the
      output signal of a LTI system whose input is just an
      impulse. Such output signal is called <term>impulse
      response</term>. Since any discrete-time (-space) signal can be
      thought of as a weighted sum of translated impulses, each sample
      that shows up to the input <emphasis>activates</emphasis> an
      impulse response whose amplitude is determined by the value of
      the sample itself. Moreover, since the impulse responses are
      activated at a distance of one sampling step from each other and
      are extended over several samples, the effect of each input
      sample is distributed over time, on a number of contiguous
      samples of the output signal. Being the system linear and
      time-invariant, the successive impulse responses sum their
      effects. In other words, the system has memory of the past
      samples, previously given as input to the system, and it uses
      such memory to influence the present.</para>
      <para id="piattop">
	To have a physical analogy, we can think of regular strokes of
	a snare drum. The response to each stroke is distributed in
	time and overlaps with the responses to the following strokes.
      </para>
      <example id="xexa">
	<para id="xexamp">Consider the signal <m:math>
	    <m:ci>x</m:ci>
	  </m:math> that is zero everywhere but at the instants <m:math>
	    <m:apply>
	      <m:minus/>
	      <m:mn>1</m:mn>
	    </m:apply>
	  </m:math>, <m:math>
	    <m:mn>0</m:mn>
	  </m:math>, and <m:math>
	    <m:mn>1</m:mn>
	  </m:math> where it has values <m:math>
	    <m:mn>1</m:mn>
	  </m:math>, <m:math>
	    <m:mn>0.5</m:mn>
	  </m:math>, and <m:math>
	    <m:mn>0.25</m:mn>
	  </m:math>, respectively. At every instant <m:math>
	    <m:ci>n</m:ci>
	  </m:math>, <m:math><m:apply>
	      <m:ci type="fn">x</m:ci>
	      <m:ci>n</m:ci>
	  </m:apply>
	  </m:math> can be expressed as <m:math>
	      <m:apply>
		<m:plus/>
	      <m:apply>
		<m:times/>
		<m:cn>1</m:cn>
		<m:apply>
		  <m:ci type="fn">δ</m:ci>
		<m:apply>
		  <m:plus/>
		  <m:ci>n</m:ci>
		  <m:cn>1</m:cn>
		</m:apply>
		</m:apply>
	      </m:apply>
	      <m:apply>
		<m:times/>
		<m:cn>0.5</m:cn>
		<m:apply>
		  <m:ci type="fn">δ</m:ci>
		  <m:ci>n</m:ci>
		</m:apply>
	      </m:apply>
	      <m:apply>
		<m:times/>
		<m:cn>0.25</m:cn>
		<m:apply>
		  <m:ci type="fn">δ</m:ci>
		<m:apply>
		  <m:minus/>
		  <m:ci>n</m:ci>
		  <m:cn>1</m:cn>
		</m:apply>
		</m:apply>
	      </m:apply>
	      </m:apply>
	  </m:math>. By linearity, the output can be obtained by
	  composition of carefully translated and weighted impulse
	  responses: <m:math>
	    <m:apply>
	      <m:eq/>
	      <m:apply>
		<m:ci type="fn">y</m:ci>
		<m:ci>n</m:ci>
	      </m:apply>
	      <m:apply>
		<m:plus/>
	      <m:apply>
		<m:times/>
		<m:cn>1</m:cn>
		<m:apply>
		  <m:ci type="fn">h</m:ci>
		<m:apply>
		  <m:plus/>
		  <m:ci>n</m:ci>
		  <m:cn>1</m:cn>
		</m:apply>
		</m:apply>
	      </m:apply>
	      <m:apply>
		<m:times/>
		<m:cn>0.5</m:cn>
		<m:apply>
		  <m:ci type="fn">h</m:ci>
		  <m:ci>n</m:ci>
		</m:apply>
	      </m:apply>
	      <m:apply>
		<m:times/>
		<m:cn>0.25</m:cn>
		<m:apply>
		  <m:ci type="fn">h</m:ci>
		<m:apply>
		  <m:minus/>
		  <m:ci>n</m:ci>
		  <m:cn>1</m:cn>
		</m:apply>
		</m:apply>
	      </m:apply>
	      </m:apply>
	    </m:apply>
	  </m:math>.
	</para>
      </example>
      <para id="xexampp">To generalize the example  <cnxn target="xexa"/> we can define the operation of 
      <term>convolution</term>.</para>
      <definition id="convolutiond">
	<term>Convolution of two signals <m:math>
	    <m:ci>h</m:ci>
	  </m:math> and <m:math>
	    <m:ci>x</m:ci>
	  </m:math>
	</term>
	<meaning> <m:math>
	    <m:apply>
	      <m:eq/>
	      <m:apply>
		<m:ci type="fn">y</m:ci>
		<m:ci>n</m:ci>
	      </m:apply>
	      <m:apply>
		<m:ci type="fn">h * x</m:ci>
		<m:ci>n</m:ci>
	      </m:apply>
	      <m:apply>
		<m:sum/>
		<m:bvar>
		  <m:ci>m</m:ci>
		</m:bvar>
		<m:lowlimit><m:apply>
		    <m:minus/>
		    <m:infinity/>
		  </m:apply>
		</m:lowlimit>
		<m:uplimit>
		  <m:infinity/>
		</m:uplimit>
		<m:apply>
		  <m:times/>
 	      <m:apply>
		<m:ci type="fn">x</m:ci>
		<m:ci>m</m:ci>
	      </m:apply>
		<m:apply>
		  <m:ci type="fn">h</m:ci>
		  <m:apply>
		    <m:minus/>
		    <m:ci>n</m:ci>
		    <m:ci>m</m:ci>
		  </m:apply>
		</m:apply>
		</m:apply>
	      </m:apply>
	    </m:apply>

	  </m:math>
</meaning>
      </definition>

      <para id="invito_conv">The operation of convolution can be fully
      understood by the explicit construction of some examples of
      convolution product. The module <cnxn document="m10087">Discrete-Time Convolution</cnxn> gives the
      graphic construction of an examples and it offers pointers to
      other examples. </para>
      <section>
	<name>Properties</name> <para id="proprietap">The properties
	of the convolution operation are well illustrated in the
	module <cnxn document="m10088">Properties of
	Convolution</cnxn>. The most interesting of such properties is
	the extension:</para>
	<rule id="estensione_conv" type="property">
	  <statement>
	   <para id="estensionepp">
	      If <m:math> <m:apply> <m:ci type="fn">x</m:ci>
	    <m:ci>n</m:ci>
	      </m:apply>
	      </m:math> is extended over <m:math>
		<m:msub>
		  <m:ci>M</m:ci>
		  <m:mn>1</m:mn>
		</m:msub>
	      </m:math> samples, and <m:math> <m:apply>
		<m:ci type="fn">h</m:ci>
		<m:ci>n</m:ci>
	      </m:apply>
	      </m:math> is extended over <m:math> <m:msub>
<m:ci>M</m:ci> <m:mn>2</m:mn> </m:msub> </m:math> samples, then the convolution product  <m:math> <m:apply> <m:ci type="fn">y</m:ci>
<m:ci>n</m:ci>
	      </m:apply>
	      </m:math> is extended over <m:math>
		<m:apply>
		  <m:plus/>		<m:msub>
		  <m:ci>M</m:ci>
		  <m:mn>1</m:mn>
		</m:msub>
		<m:msub>
		  <m:ci>M</m:ci>
		  <m:mn>2</m:mn>
		</m:msub>
                </m:apply>
		  <m:apply>
		    <m:minus/>
		    <m:cn>1</m:cn>
		  </m:apply>
	      </m:math> samples.
	    </para>
	  </statement>
	</rule>
	<para id="lungop">Therefore, the signal <emphasis>convolution
	product</emphasis> is longer than both the input signal and
	the impulse response.</para> <para id="commutap">Another
	interesting property is the commutativity of the convolution
	product, such that the input signal and the impulse response
	can change their roles without affecting the output
	signal.</para>
      </section>
    </section>
    <section>
      <name>Frequency response and filtering</name> <para id="freqrespp">The <cnxn document="m0046">Fourier
      Transform</cnxn> of the impulse response is called
      <term>Frequency Response</term> and it is represented with
      <m:math>
	  <m:apply>
	    <m:ci type="fn">H</m:ci>
	    <m:ci>ω</m:ci>
	  </m:apply>
	</m:math>. The Fourier transform of the system output is
	obtained by multiplication of the Fourier transform of the
	input with the frequency response, i.e., <m:math>
	  <m:apply>
	    <m:eq/>
	  <m:apply>
	    <m:ci type="fn">Y</m:ci>
	    <m:ci>ω</m:ci>
	  </m:apply>
	    <m:apply>
	      <m:times/>
	  <m:apply>
	    <m:ci type="fn">H</m:ci>
	    <m:ci>ω</m:ci>
	  </m:apply>
	  <m:apply>
	    <m:ci type="fn">X</m:ci>
	    <m:ci>ω</m:ci>
	  </m:apply>
	    </m:apply>
	  </m:apply>
	</m:math>.
 </para>
      <para id="sagomap">
	The frequency response shapes, in a multiplicative fashion,
	the input-signal spectrum or, in other words, it performs some
	<term>filtering</term> by emphasizing some frequency
	components and attenuating some others. A filtering can also
	operate on the phases of the spectral components, by delaying
	them of different amounts.
      </para>
      <para id="filtraggio">
	Filtering can be performed in the <emphasis>time domain
	</emphasis> (or space domain), by the operation of
	convolution, or in the <emphasis>frequency domain</emphasis>
	by multiplication of the frequency response.
      </para>
      <exercise id="primofiltro">
	<problem>
	  <para id="primofiltropp">Take the impulse response that is
	  zero everywhere but at the instants <m:math>
	    <m:apply>
	      <m:minus/>
	      <m:mn>1</m:mn>
	    </m:apply>
	  </m:math>, <m:math> <m:mn>0</m:mn> </m:math>, and <m:math>
	    <m:mn>1</m:mn> </m:math> where it has values <m:math>
	    <m:mn>1</m:mn> </m:math>, <m:math> <m:mn>0.5</m:mn>
	    </m:math>, and <m:math> <m:mn>0.25</m:mn> </m:math>,
	    respectively. Redefine the filtering operation
	    <code>filtra()</code> of the <cnxn document="m12664" target="sound_chooser">Sound Chooser</cnxn> presented in
	    the module <cnxn document="m12983">Media Representation in
	    Processing</cnxn>. In this case filtering is operated in
	    the time domain by convolution.
	  </para>
	</problem>
	<solution>
	  <code type="block">
          <![CDATA[       
	  void filtra(float[] DATAF, float[] DATA, float WC, float RO) {
	    //WC and R0 are useless, here kept only to avoid rewriting other
	    //parts of code
	    for(int i = 2; i < DATA.length-1; i++){
	      DATAF[i] = DATA[i+1] + 0.5*DATA[i] + 0.25*DATA[i-1];
	      }
	    }
             ]]>	      
	  </code>
	</solution>
      </exercise>
      <section>
	<name>Causality</name>
	<para id="causalityp">
	  The notion of causality is quite intuitive and it
	  corresponds to the experience of stimulating a system and
	  getting back a response only at future time instants. For
	  discrete-time LTI systems, this happens when the impulse
	  response is zero for negative (discrete) time
	  instants. Causal LTI systems can produce with no appreciable
	  delay an output signal <term>sample-by-sample</term>,
	  because the convolution operator acts only on present and
	  past values of the input signal. In <cnxn target="primofiltro"/> the impulse response is not causal,
	  but this is not a problem because the whole input signal is
	  already available, and the filter can process the whole
	  block of samples.
	</para>
      </section>
    </section>
    <section>
      <name>2D Filtering</name> 
      <para id="imgprcp">
	The notions of impulse response, convolution, frequency
	response, and filtering naturally extend from 1D to 2D, thus
	giving the fundamental concepts of image processing.
      </para>
      <para id="convolve2D">
      <definition id="convolution2dd">
	<term>Convolution of two 2D signals (images)</term>
	  <meaning>
	    <m:math>
	    <m:apply>
	      <m:eq/>
	      <m:apply>
		<m:ci type="fn">y</m:ci>
		<m:ci>m</m:ci>
		<m:ci>n</m:ci>
	      </m:apply>
	      <m:apply>
		<m:ci type="fn">h * x</m:ci>
		<m:ci>m</m:ci>
		<m:ci>n</m:ci>
	      </m:apply>
	      <m:apply>
		<m:sum/>
		<m:bvar>
		  <m:ci>k</m:ci>
		</m:bvar>
		<m:lowlimit><m:apply>
		    <m:minus/>
		    <m:infinity/>
		  </m:apply>
		</m:lowlimit>
		<m:uplimit>
		  <m:infinity/>
		</m:uplimit>
	      <m:apply>
		<m:sum/>
		<m:bvar>
		  <m:ci>l</m:ci>
		</m:bvar>
		<m:lowlimit><m:apply>
		    <m:minus/>
		    <m:infinity/>
		  </m:apply>
		</m:lowlimit>
		<m:uplimit>
		  <m:infinity/>
		</m:uplimit>
		<m:apply>
		  <m:times/>
 	      <m:apply>
		<m:ci type="fn">x</m:ci>
		<m:ci>k</m:ci>
		<m:ci>l</m:ci>
	      </m:apply>
		<m:apply>
		  <m:ci type="fn">h</m:ci>
		      <m:apply>
			<m:minus/>
			<m:ci>m</m:ci>
			<m:ci>k</m:ci>
		      </m:apply>
		      <m:apply>
			<m:minus/>
			<m:ci>n</m:ci>
			<m:ci>l</m:ci>
		      </m:apply>
		</m:apply>
		</m:apply>
	      </m:apply>
	    </m:apply>
	    </m:apply>
	  </m:math>
	  </meaning>
	</definition>
	If <m:math> <m:ci>x</m:ci> </m:math> is the image that we are
	  considering, it is easy to realize that convolution is
	  performed by multiplication and translation in space of a
	  <term>convolution mask</term> or <term>kernel</term>
	  <m:math> <m:ci>h</m:ci> </m:math> (it is the impulse
	  response of the processing system). As in the 1D case
	  filtering could be interpreted as a combination of
	  contiguous samples (where the extension of such cluster
	  depends on the extension of the filter impulse response)
	  that is repeated in time, sample by sample. So, in 2D space
	  filtering can be interpreted as a combination of contiguous
	  samples (pixels) in a cluster, whose extension is given by
	  the convolution mask. The so-called memory of 1-D systems
	  becomes in 2-D a sort of <emphasis>distance
	  effect</emphasis>.  </para> <para id="freqresp2Dp">As in the
	  1D case, the Fourier transform of the impulse response is
	  called <term>Frequency response</term> and it is indicated
	  by <m:math>
	  <m:apply>
	    <m:ci type="fn">H</m:ci>
	      <m:ci>		<m:msub>
		  <m:mi>ω</m:mi>
		  <m:mn>X</m:mn>
		</m:msub>
	      </m:ci> 
	      <m:ci>		<m:msub>
		  <m:mi>ω</m:mi>
		  <m:mn>Y</m:mn>
		</m:msub>
	      </m:ci> 
	  </m:apply>
	</m:math>. The Fourier transform of the system output is
	obtained by Fourier-transforming the input and multiplying the
	result by the frequency response. <m:math>
	  <m:apply>
	    <m:eq/>
	  <m:apply>
	    <m:ci type="fn">Y</m:ci>
	      <m:ci>		<m:msub>
		  <m:mi>ω</m:mi>
		  <m:mn>X</m:mn>
		</m:msub>
	      </m:ci> 
	      <m:ci>		<m:msub>
		  <m:mi>ω</m:mi>
		  <m:mn>Y</m:mn>
		</m:msub>
	      </m:ci> 
	  </m:apply>
	    <m:apply>
	      <m:times/>
	  <m:apply>
	    <m:ci type="fn">H</m:ci>
	      <m:ci>		<m:msub>
		  <m:mi>ω</m:mi>
		  <m:mn>X</m:mn>
		</m:msub>
	      </m:ci> 
	      <m:ci>		<m:msub>
		  <m:mi>ω</m:mi>
		  <m:mn>Y</m:mn>
		</m:msub>
	      </m:ci> 
	  </m:apply>
	  <m:apply>
	    <m:ci type="fn">X</m:ci>
	      <m:ci>		<m:msub>
		  <m:mi>ω</m:mi>
		  <m:mn>X</m:mn>
		</m:msub>
	      </m:ci> 
	      <m:ci>		<m:msub>
		  <m:mi>ω</m:mi>
		  <m:mn>Y</m:mn>
		</m:msub>
	      </m:ci> 
	  </m:apply>
	    </m:apply>
	  </m:apply>
	</m:math>.
 </para>    
      <exercise id="convolve2dprob">
	<problem>
	  <para id="convolve2dprobp">Consider the Processing code of
	  the <link src="http://www.processing.org/learning/examples/blur.html">blurring
	  example</link> and find the lines that implement the
	  convolution operation.
	  </para>
	</problem>
	<solution>
	  <code type="block">
          <![CDATA[       
for(int y=0; y<height; y++) { 
  for(int x=0; x<width/2; x++) { 
    float sum = 0; 
    for(int k=-n2; k<=n2; k++) { 
      for(int j=-m2; j<=m2; j++) { 
        // Reflect x-j to not exceed array boundary 
        int xp = x-j; 
        int yp = y-k; 
        //... omissis ...
	//auxiliary code to deal with image boundaries   
        sum = sum + kernel[j+m2][k+n2] * red(get(xp, yp)); 
      } 
    } 
    output[x][y] = int(sum);  
  } 
} 
             ]]>	      
	  </code>
	</solution>
      </exercise>
</section>
  </content>
  
</document>
