Exercise of share options

7 July 2017

Issue of new shares / Exercise of Share Options

The Board of Stilo International plc (“Stilo” or the “Company”) (LSE:STL), the AIM quoted software and cloud services company, announces that it has today issued 87,000 new ordinary shares of 1p each in the company (”Ordinary Shares”) following notification of the exercise of share options by an employee. The exercise price of the new shares is 1.5 pence per share.

Application has been made for the 87,000 Ordinary Shares to be admitted to trading on AIM and it is expected that admission will take place on 13th July 2017.

The Ordinary Shares will rank pari passu with the existing shares of the Company.  Following allotment of the Ordinary Shares, the total issued share capital of the Company will be 113,930,470 ordinary shares.

For the purposes of the Financial Conduct Authority’s Disclosure and Transparency Rules (“DTRs”), the issued ordinary share capital of Stilo following this allotment will consist of 113,930,470 ordinary shares with voting rights attached (one vote per share). There are no shares held in treasury.  This total voting rights figure may be used by shareholders as the denominator for the calculation by which they will determine whether they are required to notify their interest in, or a change to their interest in, Stilo under the DTRs.

ENQUIRIES

Stilo International plc
Les Burnham, Chief Executive T +44 1793 441 444
Liam O’Donoghue, Company Secretary T +44 20 7583 8304

SPARK Advisory Partners Limited (Nominated Adviser)
Neil Baldwin T +44 203 368 3554
Mark Brady  T +44 203 368 3551

SI Capital (Broker)
Andy Thacker
Nick Emerson
T +44 1483 413500


SDL Connect 2017 | October 25-26, San Jose, CA

Stilo is pleased to be one of the bronze sponsors at SDL Connect 2017, being held October 25-26 in San Jose, California - SDL's annual flagship event that offers attendees two packed days of learning, networking and connecting!

Prepare for the future of digital globalization at SDL Connect!

Join us for two packed days at SDL Connect, our annual flagship event, to discuss globalization, localization, AI, web and technical content management, and much more. We’ll also be commemorating how, for the past 25 years, SDL has been helping companies change the way they communicate with customers, and we’ll share our vision for the next 25.

Find out more about SDL Connect and register to attend.


Migrate Live Demo | July 12, 2017, 9am PDT

Join Stilo’s Conversion Services Manager, Helen St. Denis, for a live 30-minute demonstration of the Migrate XML cloud content conversion service. Migrate is a unique cloud service that enables technical authoring teams to automate the conversion of their content from various source formats including XML/SGML/HTML, FrameMaker, Word, Author-it, InDesign, RoboHelp and DocBook to DITA and custom XML. It provides greater control over conversion quality, immediate turnaround times and operates on a low-cost, pay-as-you-use basis.

See why Migrate is the conversion service of choice for organizations including Altera, Cisco, Dell, EMC, Extreme Networks, IBM, Qualcomm, Teradata, Varian Medical Systems, Webtrends and many more.

Find out more about the Migrate XML cloud content conversion service
Convert sample document to DITA for free

Watch recording

CIDM Best Practices 2017 | September 11-13, Burlington, VT

Stilo is pleased to support the CIDM Best Practices conference - the premier annual conference for managers of information development, whether it be for user and product support, or training. Discover how organizations are pursuing dynamic publishing, content management, social media, enterprise-wide information creation through wikis and blogs, and much more.  In its 19th year, the Best Practices Conference will be held in Burlington, Vermont.

Visit the conference website to find out more and to register!  (Register on or before July 4th and save $170!)


CMS/DITA Europe 2017 | October 30-31, Berlin, Germany

Stilo is pleased to once again be one of the sponsors at this year’s Content Management/ DITA Europe conference being held for the first time in Berlin, Germany from October 30-31.

Join colleagues from around the world for two days of career-empowering knowledge, practices, networking, and practical solutions. With insights from over 30 speakers, an exhibit hall packed with the best content management solutions, and the very best in industry networking, CMS/DITA Europe is designed with your specific needs in mind—providing strategies you can use immediately as you create and manage technical content, and expanding your professional network with information development experts from around the world.

Whether you’re a Novice or a Master, you’ll find sessions that provide inspiration and practical skills, covering topics such as:

  • Implementing a component Content Management System
  • Adopting DITA, Markdown, or other XML structures
  • Effectively reusing content
  • Creating corporate taxonomies
  • Incorporating social media strategies
  • Managing teams
  • Collaborating across the enterprise

Those new to content management and/or DITA will find guidance for starting their journey, while for experts, our program offers ways to continue pushing the boundaries. Join us for a chance to share what you already know and find out about things you don’t!

Stop by our booth in the expo hall and ask us for a demo of Migrate, our cloud XML conversion service which enables technical authoring teams to convert content from source formats including FrameMaker and Word to XML DITA or AuthorBridge, our web-based XML editor which provides subject matter experts with a Guided & Fluid authoring experience without requiring any knowledge of XML or DITA.

We're very pleased to announce that Stilo's Patrick Baker, VP Development & Professional Services, has been selected to make the following presentations at CMS/DITA Europe 2017 - be sure to add them to your schedule!

Monday, October 30, 2017 | 10:40 - 11:20am
DITA: Start small, grow big using open source tools

Abstract | You’re considering using DITA and would like to try it out without incurring significant upfront costs, but also keeping your options open longer-term. Where do you start? How will you approach the challenges of content creation, content management, and publishing your content? There are in fact plenty of options. The good news is that XML and DITA are open standards. This has led to a healthy ecosystem with quality commercial and inter-operable open source tools, that do away with vendor lock-in and keep operating costs down. We will discuss the three challenges, show an example of how end-to-end solutions can be built based upon Git and other open source tools. In fact, the result may be better than you’d expect.

Monday, October 30, 2017 | 4:20 - 5:00pm | Technology Test Kitchen
Recipe for success: Guiding SMEs to make contributions in DITA - Just add AuthorBridge

Abstract | Join our Technology Test Kitchen to try out AuthorBridge, Stilo’s web based XML editor that enables SMEs to easily create structured content without requiring any knowledge of DITA or its complexities. Its unique architecture provides a Guided + Fluid authoring experience that sets it apart from other XML editors.

Experience level:

  • No knowledge of DITA or XML is required

What you’ll need to bring:

  • All you need to bring is your laptop! We will provide sample content
  • If you prefer, you can bring your own DITA content to work with

What you’ll take away:

  • The knowledge of how to edit and review existing DITA topics and to quickly create new ones in a collaborative environment between SMEs and technical authors
  • A free 30-day trial of AuthorBridge, which you can continue to use following the conference

 

Find out more and register for CMS/DITA Europe 2017


Exercise of share options

8 June 2017

Issue of new shares / Exercise of Share Options

The Board of Stilo International plc (“Stilo” or the “Company”) (LSE:STL), the AIM quoted software and cloud services company, announces that it has today issued 75,000 new ordinary shares of 1p each in the company (”Ordinary Shares”) following notification of the exercise of share options by an employee. The exercise price of the new shares is 1.5 pence per share.

Application has been made for the 75,000 Ordinary Shares to be admitted to trading on AIM and it is expected that admission will take place on 14th June 2017.

The Ordinary Shares will rank pari passu with the existing shares of the Company.  Following allotment of the Ordinary Shares, the total issued share capital of the Company will be 113,843,470 ordinary shares.

For the purposes of the Financial Conduct Authority’s Disclosure and Transparency Rules (“DTRs”), the issued ordinary share capital of Stilo following this allotment will consist of 113,843,470 ordinary shares with voting rights attached (one vote per share). There are no shares held in treasury.  This total voting rights figure may be used by shareholders as the denominator for the calculation by which they will determine whether they are required to notify their interest in, or a change to their interest in, Stilo under the DTRs.

ENQUIRIES

Stilo International plc
Les Burnham, Chief Executive T +44 1793 441 444
Liam O’Donoghue, Company Secretary T +44 20 7583 8304

SPARK Advisory Partners Limited (Nominated Adviser)
Neil Baldwin T +44 203 368 3554
Mark Brady  T +44 203 368 3551

SI Capital (Broker)
Andy Thacker
Nick Emerson
T +44 1483 413500


Result of AGM 18 May 2017

18 May 2017

Stilo International plc (AIM:STL), the AIM quoted software and cloud services company, announces that all resolutions proposed at its AGM held earlier today were duly passed without amendment.

ENQUIRIES

Stilo International plc
Les Burnham, Chief Executive T +44 1793 441 444
Liam O’Donoghue, Company Secretary T +44 20 7583 8304

SPARK Advisory Partners Limited (Nominated Adviser)
Neil Baldwin T +44 203 368 3554
Mark Brady  T +44 203 368 3551

SI Capital (Broker)
Andy Thacker
Nick Emerson
T +44 1483 413500


Chairman’s AGM statement 18 May 2017

18 May 2017

Stilo International plc (“Stilo” or the “Company”) (AIM:STL) is holding its Annual General Meeting later today. The Company provides software tools and cloud services that help organisations create and process content in XML format, so that it can be more easily stored, managed, re-used, translated and published to multiple print and digital channels.

At the meeting Chairman David Ashman will make the following statement:

“Following the launch of AuthorBridge v2.0 in February 2017, we have been receiving very encouraging feedback from trial users. However, there are still some important aspects of development that need to be undertaken over the coming months and this continues to be a high priority activity for the Company.

As a consequence, AuthorBridge is not expected to contribute significantly to sales revenues in 2017. Rather, our goal in 2017 is to implement AuthorBridge at a number of key customer sites and in so doing, provide a solid foundation upon which we can build future business.

Otherwise, the market for Migrate DITA conversion services and OmniMark software remains steady, and overall Company trading is in line with management expectations.

The Company remains un-geared, and cash balances at 30 April 2017 stood at £1,560,000 (31 December 2016: £1,466,000). Current levels of cash will serve to fund additional development, sales and marketing efforts as we look to grow our portfolio of solutions and enter new market sectors. It will also be used to assist with potential acquisitions, whilst providing an appropriate financial reserve for the business. Ongoing, it is the Board’s intention to maintain a progressive dividend policy with scope for special one-off dividends as may be deemed appropriate from time to time.

Subject to approval by shareholders at the meeting, a final dividend for the year ended 31 December 2016 of 0.05 pence per Ordinary Share will be paid on 23 May 2017 to those shareholders on the register at 21 April 2017.”

ENQUIRIES

Stilo International plc
Les Burnham, Chief Executive T +44 1793 441 444
Liam O’Donoghue, Company Secretary T +44 20 7583 8304

SPARK Advisory Partners Limited (Nominated Adviser)
Neil Baldwin T +44 203 368 3554
Mark Brady  T +44 203 368 3551

SI Capital (Broker)
Andy Thacker
Nick Emerson
T +44 1483 413500


OmniMark design principles

  • The streaming paradigm
  • Rules-based programming
  • Hierarchical markup parsing model
  • Powerful pattern matching
  • Referents

The streaming paradigm

The streaming paradigm is an approach to programming that concentrates on describing the process to be applied to a piece of data, and on processing data directly as it streams from one location to another. In the streaming model, the use of data structures to model input data is eliminated, and the use of data structures to model output is greatly reduced. This keeps to a minimum the use of system resources when processing large volumes of data. As a side-effect, because the processing requirements are consistent, system performance on larger data sets can be predicted with a great deal of accuracy. A program will run with equal success on a 2 kilobyte file or a 2 gigabyte file.

OmniMark has an abstracted streaming model which allows a stream to be attached to different sources of input and output — files, databases and messages — with a minimum of effort. This abstraction also allows code processing the content itself to be dissociated completely from the problems of managing or even knowing about details of the input or output type, with obvious productivity and code simplification benefits.

An application may have multiple input streams open to permit data integration. Multiple output streams may also be used to feed different targets. For instance a complex application may be taking a stream fed from a file, integrating that input with a stream fed by accessing a database and outputting the data to multiple systems (potentially in different formats).

Rules-based programming

OmniMark incorporates a declarative scripting language. This means that an application is constructed of rules for dealing with events which are triggered by the recognition of patterns of data coming into the program from a stream. In dealing with content the individual pieces of content are well known, the order in which they occur is not. This arbitrariness of content is one of its basic properties and rules are the best mechanism for dealing with it. OmniMark’s rules may be triggered by data events generated by the two types of built-in processors, the pattern processor and the markup processor. The markup processor is tightly coupled with markup parsing.

The two processors may be used in conjunction to process a single piece of content to create powerful hybrid applications; where XML is being processed and complex pattern matching is used upon the content in the markup – the text in the elements. The pattern processor may also be used ahead of the markup processor to prepare content for parsing – converting non-XML into XML for instance. The output stream of the pattern processor is fed in as the input stream of the markup processor.

All of these features are implemented in an elegant framework, which results in applications consisting of well-delineated functional code blocks both in terms of readability and actual functionality, thus producing an easily maintained application.

Hierarchical markup parsing model

Many people concerned with XML will be familiar with, or will at least have heard of, SAX and DOM models for processing. SAX is an event-based model and DOM is tree-based. OmniMark employs a third model – hierarchical. Like SAX, OmniMark leverages an event-based model, but where SAX would generate three events for an element (the start, the content and the end) OmniMark generates only one, treating the occurrence of the whole element as a single event to activate a rule. Since elements can be nested, a hierarchy of activated rules will be created, modeling the structure of the content. This simple model is easy to understand and the resulting process flow is clear, concise and thus easy to maintain.

The event-based parsing model fits neatly with the streaming approach to processing content, with the markup processor receiving a stream of data and triggering events as the elements are found, without needing to buffer or decompose the input. Therefore this model supports the design considerations for OmniMark of remaining scalable and performant when processing massive data sets or receiving high volumes of content.

In conjunction with the triggering of the rules, the markup processor maintains the current element context for the set of elements being processed at any instant. This allows the application to query and make decisions based on data about that context including the attribute values associated with the elements being processed and their parents. This mechanism has been shown to handle the vast majority of content processing requirements. However, by using other features of OmniMark this may be augmented should it be necessary – for instance a tree of all elements accessed may be constructed in the application for later manipulation.

Powerful pattern matching

The OmniMark pattern processor implements a pattern matching language that is both powerful and easy to use. Based upon an optimized regular expression mechanism, it has many other features, including:

  • Maintaining context. The same pattern may have different meanings in different contexts. Therefore context needs to be maintained to allow different rules to fire in different situations.
  • The ability to lookahead for patterns without actually processing the values. This allows program flow to be changed, before the pattern is reached, to allow the pattern to be processed in the right context.
  • Complex pattern matching procedures (i.e., independently-called functions). This allows sophisticated pattern matching to be encapsulated and reused.
  • Nested pattern matching (matching a pattern within a pattern).

The pattern processor will activate the associated rule when a pattern defined has been matched. These features are encapsulated in a language that is very English-like, making it clear, easy to comprehend application functionality, which simplifies both development and maintenance.

Referents

Often the order in which content is received as input is not the order in which it is required for output. OmniMark’s patented referent mechanism allows a placeholder to be inserted in the output stream and its value supplied later when it is available. The streaming mechanism handles the buffering of output containing unresolved placeholders. The whole referent mechanism may be scoped and nested so that buffering is kept to a minimum. Code that is processing content needs no knowledge of the mechanism; a referent is just like any other target. The major benefit of this mechanism is that it maintains the efficiency of the streaming model while enabling powerful re-ordering functionality that would otherwise be severely constrained. Referents are a key innovation within the OmniMark language and it is one reason why OmniMark is so successful at blending power and performance.


Breaking lines on your own with OmniMark

By Jacques Légaré, Senior Software Developer and Mario Blažević, Senior Software Developer
1. Motivation

OmniMark has had line-breaking functionality built-in ever since the XTRAN days. This functionality can be used to provide rudimentary text formatting capabilities. The language-level support for line-breaking is described quite thoroughly in the language documentation.

OmniMark’s language-level line-breaking support is very simple to use, and aptly supports the use-case where all the output of a program needs to be similarly formatted. Where the performance is less stellar, however, is when line-breaking needs to be activated and deactivated on a fine-grained level. The reason for this is simple: when line-breaking is disabled (say, using the h modifier), OmniMark cannot predict when it might be reactivated. As a result, it still needs to compute possible line-breaking points, just in case. As efficient as OmniMark might be, this can cause a significant reduction in performance, sometimes by as much as 15%.

As of version 8.1.0, the OmniMark compiler can detect some cases when line-breaking functionality is not being used, and thereby optimize the resulting compiled program to by pass line-breaking computations. However, the compiler cannot make this determination in general: this is an undecidable problem. For instance, consider the following somewhat contrived example:

replacement-break ” “ “%n”process do sgml-parse document scan #main-input output “%c” doneelement #implied output “%c” element “b” local stream s set s with (break-width 72) to “%c” output s

Note that line-breaking is only activated in the element rule for b, and so line-breaking will only be activated if the input file contains an element b. The OmniMark compiler cannot be expected to predict what the input files might contain when the program is executed!

Another issue with OmniMark’s built-in line-breaking is that it does not play well with referents. Specifically, consider the following program:

replacement-break ” “ “%n”process local stream sopen s as buffer with break-width 32 to 32 using output as s do xml-parse scan #main-input output “%c” done close s element #implied output “%c”  || “.” ||* 64

This program puts a hard limit of 32 characters on the maximum length of lines output to s. When this program is executed, a run-time error is triggered in the body of the element rule, where we attempt to output 64 periods. On the other hand, consider the following similar program:

replacement-break ” “ “%n”process local stream sopen s as buffer with (referents-allowed & break-width 32 to 32) using output as s do xml-parse scan #main-input output “%c” done close s set referent “a” to “.” ||* 64 output s element #implied output “%c”  || referent “a”

This program accomplishes virtually the same task, but instead uses a referent to output the string of periods. In this case, no run-time error is triggered: the line-breaking constraints have been silently violated.

Because of these issues, it is better to use OmniMark’s built-in line-breaking only when necessary, whereas in other cases to implement line-breaking using other language constructs.

The remainder of this article discusses how to simulate line-breaking on PCDATA using string sink functions.

2. string sink functions

string sink function is a function that can be used as the destination for strings. In a very real sense, a string sink function is the complement of a string source function, which is used as the source of strings. While a string source function outputs its strings to #current-output, a string sink function reads its strings from #current-input.

string sink function is defined much like any other function in OmniMark, the only difference being that the return type is string sink: for example,

define string sink function dev-null as void #current-input

This is a poor man’s #suppress, soaking up anything written to it.

string sink function can have any of the properties normally used to define functions in OmniMark: e.g., it can be  overloadeddynamicetc …. The argument list of a string sink function is unrestricted. However, in the body of a string sink function, #current-output is unattached.The form of OmniMark’s pattern matching and markup parsing capabilities makes string sink functions particularly convenient for writing filters, taking their #current-input, processing it in some fashion, and writing the result out to some destination. However, since #current-output is unattached inside the function, we need to pass the destination as an argument. For this, we use a value string sink argument. For example, a string sink function that indents its input by a given amount might be written

define string sink function
 indent (value integer     i,
 value string sink s)
 as
 using output as s
 do
 output ” “ ||* i       repeat scan #current-input
 match “%n”
 output “%n” || ” “ ||* i      match any-text* => t
 output t
 again
 done

The function indent could then be used like any other string sink:

; …
 using output as indent (5, #current-output)
 do sgml-parse document scan #current-input
 output “%c”
 done

(The ability to pass #current-output as a value string sink argument is new in OmniMark 8.1.0.)

You can find out more about string sink functions in the language documentation.

3.  Line-breaking in OmniMark

We can use a pair of string sink functions to simulate to some extent OmniMark’s built-in line-breaking functionality. The benefit of this approach is that it impacts the program’s performance only where it is used.

3.1. Simulating insertion-break

To simulate the effect of insertion-break on PCDATA we need to scan the input and grab as many characters as we can up to a specified width. If we encounter a newline in the process, we stop scanning. Otherwise, we output the characters we found, and append a line-breaking sequence provided by the user.

define string sink function
 insertion-break       value string      insertion
 width value integer     target-width
 into value string sink destination
 as

We can start by sanitizing our arguments:

assert insertion matches any ** “%n”
 message “The insertion string %”” || insertion
 || “%” does not contain a newline character %”%%n%”.”

This assertion is not strictly necessary. However, OmniMark insists that the line-breaking sequence contain a line-end character, and so we do the same.

We can grab a sufficient number of characters from #current-input by using OmniMark’s counted occurrence pattern operator:

using output as destination
 repeat scan #current-input
 match any-text{1 to target-width} => l (lookahead “%n” => n)?
 output l

The use of lookahead at the end of the pattern allows us to verify if a %n is upcoming: we should only output the line-breaking sequence if the characters we grabbed are not followed by a %n.

output insertion
 unless n is specified   match “%n”
 output “%n”
 again

We can then use this to break the text output from the markup parser: for example,

process
 using output as insertion-break “%n” width 20 into #current-output
 do sgml-parse document scan #main-input
 output “%c”
 done

3.2. Simulating replacement-break

Simulating insertion-break on PCDATA is straightforward, because it can insert a line-breaking sequence whenever it sees fit. On the other hand, replacement-break is slightly more complex, since it must scan its input for a breakable point. For clarity, the characters between two breakable points will be referred to aswords; if the breakable points are defined by the space character, they are effectively words.

define string sink function
 replacement-break       value string      replacement
 width value integer     target-width
 to value integer     max-width    optional
 at value string      original     optional initial { ” “}
 into value string sink destination
 as

The argument original is used to specify the character that delimits words; the argument is optional, as a space character seems like a reasonable default. target-width specifies the desired width of the line. max-width, if specified, gives the absolute maximum acceptable line width; if a line cannot be broken within this margin, an error is thrown. Finally, the argument replacement gives the line-breaking sequence.

As before, we start by ensuring our arguments have reasonable values:

assert length of original = 1
 message “Expecting a single character string,”
 || ” but received %”” || original || “%”.”   assert replacementmatches any ** “%n”
 message “The replacement string %”” || replacement
 || “%” does not contain a newline character %”%%n%”.”

The second assertion is repeated from above, for the same reasons as earlier: OmniMark insists that the replacement string contain a newline, and so we will do the same. The first assertion insists that breakable points be defined by a single character; again, this is a carry-over from OmniMark’s implementation.

For replacement-break, the pattern is very different from that of insertion-break: in that case, we could consume everything with a single pattern, using a counted occurrence. This does not suffice with replacement-break: rather, we have to consume words until we reach target-width.

using output as destination
 do
 local stream line initial { “” }

The stream line will be used to accumulate text from one iteration to another.

repeat scan #current-input
 match ((original => replaceable)? any-text
 ** lookahead (original | “%n” | value-end)) => t

The pattern in the match clause picks up individual words. If the line length is still below target-width, we can simply append the word to the current line and continue with the next iteration:

do when length of line + length of t < target-width
 set line with append to t

If this is not the case, we can output the text we have accumulated thus far, so long as it does not surpassmax-width

else when max-width isnt specified
 | length of line < max-width
 output line
 output replacement
 when replaceable is specified            set line to t droporiginal?

If all else fails, we could not find an acceptable breakable point in the line: OmniMark throws an error in this case, so we will do the same.

else
 not-reached message “Exceeded maximum line width”
 || ” of %d(max-width) characters.%n”
 || “The line is %”” || line || “%”.%n”         done

Our string sink function needs a few more lines to be complete. For one, our previous pattern does not consume any %n that it might encounter. In this case, we should flush the accumulated text, and append a%n:

match “%n”
 output line || “%n”
 set line to “”
 again

Lastly, when the repeat scan loop finishes, there may be some text left over in line, which needs to be emitted:

output line
 done

Just as was the case previously in Section 3.1, “Simulating insertion-break”, we can use our function to break text output from the markup parser: for example,

process
 using output as replacement-break “%n” width 10 to 15 into #main-output
 do sgml-parse document scan #main-input
 output “%c”
 done
4.  Going further

We demonstrated in Section 1, “Motivation” that referents and line-breaking did not play well together: in fact, a referent could be used to silently violate the constraints stated by a break-width declaration. In the case of our string sink simulations, referents are a non-issue: a referent cannot be written to an internal string sink function, which effectively closes the loophole.

OmniMark’s built-in line-breaking functionality can be manipulated using the special sequences %[ and %]: by embedding one of these in a string that is output to a stream, we can activate or deactivate (respectively) line-breaking. The easiest way of achieving this effect with our string sink functions would be to add a read-only switch argument called, say, enabledviz

define string sink function
 insertion-break         value     string      insertion
 width value     integer     target-width
 enabled read-only switch      enabled      optional
 into value     string sink destination
 as

and similarly for replacement-break. We could then use the value of this shelf item to dictate whether the functions should actively break their input lines, or pass them through unmodified.

Breaking lines using string sink functions in this fashion is really only the beginning. For instance, we could envision a few simple modifications to replacement-break that would allow it to fill paragraphs instead of breaking lines: it would attempt to fill out a block of text so that all the lines are of similar lengths.

The code for this article is available for download.