-Dbugger_off

As someone titled a compiler switch during one of my first jobs. Been looking for a way to specify commandline switches on a CMake project under VS2017? Missing Project/Properties? This is what you’re loking for : https://blogs.msdn.microsoft.com/vcblog/2016/10/05/cmake-support-in-visual-studio/#debug-code

In other news, discovered my GPS USB stick automatically adds leap seconds to GPS time. Unfortunately it only adds them up to 2015 (bought it last year). Which is logical, but a mite irritating. Would have been better if they didn’t add them at all to a certain extent.

Not simplex enough for me

I forked Stephen M. Cameron’s C port of Kurt Spencer’s open simplex noise while on the train to make it play nicely with Visual C++, which while it’s reasonably up to date with C++ is definitely stuck in the last century when it comes to C (it’s still shy of C99). I had a go at making of it ANSI C friendly too while I was there. The repo is here : Open Simplex Github fork.

EDIT : Changes now pushed up to main repository.

Git Central

Due to the unexpected shutdown of Google Code, the tools23d repository is now on Github (http://github.com/davegorst/tools23d.git) – THE place for gits of all kinds. Make new friends! Meet other gits. You can do it all at https://github.com/davegorst/tools23d. Call now! Call charges apply.

UPDATE : Anaglyph rendering now works (in addition to shutter glasses), so it’s now possible to create pics/videos that work with the coloured specs you used to get at the cinema.

Release early/release often/release terrible

The code for stitching together 3D images from left/right pairs is up on Google Code (https://code.google.com/p/tools23d/). Looking through it I can see about 80 things that are a mite shonky (not least that the wraparound code isn’t done yet), but it’s sort of my mid-year resolution to actually release stuff rather than tinker for a decade and a day. It’s standard ANSI C/C89 if you fancy compiling it up.

Yes – it would have been here a sight quicker using yer standard C++/Boost type malarkey, but I did it in bog-standard C basically because a) I wanted to check I could still write non-objecty code and hadn’t permanently sprained my brain, and b) when I started writing it when I only had a Raspberry Pi as a development machine and the compile times were eating away at my sanity.

Currently still faffing with the Pi/Arduino side, -will bung that up too soon-ish once I’m sure I’ve got both camera’s synced properly …

Emscriptening

Hmm – so once I’ve got the timelapse/SDL2/3D stuff out of the way I’m looking to kick off another project – investigating adding a VXML backend to Emscripten (a source-to-source compiler built on top of LLVM). This should give us the following advantages :

  • Retargetability
  • Lower skills barrier
  • Built-in optimisation

It’ll probably be a personal project (unless I can get anyone to pay me for it of course …). Have set up a Google Code project for the photo-to-3D code – will upload once I’ve got a few more kinks knocked out of it (“release early and release often” and all that). I’m trying not run afoul of the usual twin tag team of perfectionism and explosive feature-creep, but it’s difficult – even on such a small project.

Reading SVG paths using Boost

Here’s a bit of code to read SVG paths – it’s from my code to convert vector graphics to OpenVG commands and 6809 assembler for the Vectrex (among other things). However, as that’s progressing at around the speed of continental drift I may as well put it up here in the hope that someone else may get some use out of it. It uses Boost.Spirit, which is an excellent method of turning EBNF grammars into something that looks like Martian hieroglyphics, should you be so minded :

// Literals.</pre>
<pre>
nsParser::rule<sIter> flag;
nsParser::rule<sIter> comma;
nsParser::rule<sIter> sign;
nsParser::rule<sIter> wsp;
nsParser::rule<sIter> digit;
nsParser::rule<sIter> dot;
nsParser::rule<sIter> comma_wsp;

nsParser::rule<sIter> exponent;
nsParser::rule<sIter> digit_sequence;
nsParser::rule<sIter> integer_constant;
nsParser::rule<sIter> floating_point_constant;
nsParser::rule<sIter> fractional_constant;

nsParser::rule<sIter> number;
nsParser::rule<sIter> nonnegative_number;
nsParser::rule<sIter> coordinate;
nsParser::rule<sIter> coordinate_pair;

// Path commands.

nsParser::rule<sIter> closepath;

nsParser::rule<sIter> lineto_argument_sequence;
nsParser::rule<sIter> lineto;
nsParser::rule<sIter> moveto_argument_sequence;
nsParser::rule<sIter> moveto;

nsParser::rule<sIter> horizontal_lineto_argument_sequence;
nsParser::rule<sIter> horizontal_lineto;
nsParser::rule<sIter> vertical_lineto_argument_sequence;
nsParser::rule<sIter> vertical_lineto;

nsParser::rule<sIter> curveto;
nsParser::rule<sIter> curveto_argument;
nsParser::rule<sIter> curveto_argument_sequence;

nsParser::rule<sIter> smooth_curveto;
nsParser::rule<sIter> smooth_curveto_argument;
nsParser::rule<sIter> smooth_curveto_argument_sequence;

nsParser::rule<sIter> quadratic_bezier_curveto;
nsParser::rule<sIter> quadratic_bezier_curveto_argument;
nsParser::rule<sIter> quadratic_bezier_curveto_argument_sequence;

nsParser::rule<sIter> smooth_quadratic_bezier_curveto;
nsParser::rule<sIter> smooth_quadratic_bezier_curveto_argument;
nsParser::rule<sIter> smooth_quadratic_bezier_curveto_argument_sequence;

nsParser::rule<sIter> elliptical_arc;
nsParser::rule<sIter> elliptical_arc_argument;
nsParser::rule<sIter> elliptical_arc_argument_sequence;

nsParser::rule<sIter> drawto_command;
nsParser::rule<sIter> drawto_commands;
nsParser::rule<sIter> moveto_drawto_command_group;
nsParser::rule<sIter> moveto_drawto_command_groups;
nsParser::rule<sIter> svg_path;

CParsePath::CParsePath ()
{
    InputString = "";
    itStart = InputString.begin ();
    itEnd = InputString.end ();

    // Define parser elements.

    flag = (nsParser::lit ('0') | nsParser::lit ('1'));
    comma = (nsParser::lit (','));
    sign = (nsParser::lit ('+') | nsParser::lit ('-'));

    wsp = (
        nsParser::lit (' ') |
        nsParser::lit ('\t') |
        nsParser::lit ('\r') |
        nsParser::lit ('\n')
    );

    digit = (
        nsParser::lit ('0') |
        nsParser::lit ('1') |
        nsParser::lit ('2') |
        nsParser::lit ('3') |
        nsParser::lit ('4') |
        nsParser::lit ('5') |
        nsParser::lit ('6') |
        nsParser::lit ('7') |
        nsParser::lit ('8') |
        nsParser::lit ('9')
    );

    dot = nsParser::lit ('.');



// 0 or 1 : EBNF ? Qi -
// 0 or more : EBNF * Qi *
// 1 or more : EBNF + Qi +

    comma_wsp = (+wsp >> -comma >> *wsp) | (comma >> *wsp);
/*
digit-sequence:
    digit
    | digit digit-sequence

    NOTE : Don't understand - isn't this just "one or more"?
*/

    digit_sequence = (+digit);
    exponent = ((nsParser::lit ('E') | nsParser::lit ('e')) >> -sign >> digit_sequence);
    integer_constant = digit_sequence;
    fractional_constant = ((-digit_sequence >> dot >> digit_sequence) | (digit_sequence >> dot));
    floating_point_constant = ((fractional_constant >> -digit_sequence) | (digit_sequence >> exponent));
    number = ((-sign >> integer_constant) | (-sign >> floating_point_constant));
    nonnegative_number = (integer_constant | floating_point_constant);
    coordinate = number;
    coordinate_pair = (coordinate >> -comma-wsp >> coordinate);

    closepath = (nsParser::lit (ABSOLUTE_CLOSEPATH) | nsParser::lit (RELATIVE_CLOSEPATH));
    lineto_argument_sequence = ((coordinate_pair) | (coordinate_pair >> -comma_wsp >> lineto_argument_sequence));
    lineto = (nsParser::lit (ABSOLUTE_LINETO) | nsParser::lit (RELATIVE_LINETO)) >> *wsp >> lineto_argument_sequence;
    moveto_argument_sequence = ((coordinate_pair) | (coordinate_pair >> -comma_wsp >> lineto_argument_sequence));
    moveto = (nsParser::lit (ABSOLUTE_MOVETO) | nsParser::lit (RELATIVE_MOVETO)) >> *wsp >> moveto_argument_sequence;
    horizontal_lineto_argument_sequence = ((coordinate) | (coordinate >> -comma_wsp >> horizontal_lineto_argument_sequence));
    horizontal_lineto = (nsParser::lit (ABSOLUTE_HLINETO) | nsParser::lit (RELATIVE_HLINETO)) >> *wsp >> horizontal_lineto_argument_sequence;
    vertical_lineto_argument_sequence = ((coordinate) | (coordinate >> -comma_wsp >> vertical_lineto_argument_sequence));
    vertical_lineto = (nsParser::lit (ABSOLUTE_VLINETO) | nsParser::lit (RELATIVE_VLINETO)) >> *wsp >> vertical_lineto_argument_sequence;
    curveto_argument = (coordinate_pair >> -comma_wsp >> coordinate_pair >> -comma_wsp >> coordinate_pair);
    curveto_argument_sequence = ((curveto_argument) | (curveto_argument >> -comma-wsp >> curveto_argument_sequence));
    curveto = ((nsParser::lit (ABSOLUTE_CURVETO) | nsParser::lit (RELATIVE_CURVETO)) >> *wsp >> curveto_argument_sequence);
    smooth_curveto_argument = (coordinate_pair >> -comma_wsp >> coordinate_pair >> -comma_wsp >> coordinate_pair);
    smooth_curveto_argument_sequence = ((smooth_curveto_argument) | (smooth_curveto_argument >> -comma-wsp >> smooth_curveto_argument_sequence));
    smooth_curveto = ((nsParser::lit (ABSOLUTE_SCURVETO) | nsParser::lit (RELATIVE_CURVETO)) >> *wsp >> smooth_curveto_argument_sequence);
    quadratic_bezier_curveto_argument = (coordinate_pair >> -comma_wsp >> coordinate_pair);
    quadratic_bezier_curveto_argument_sequence = ((quadratic_bezier_curveto_argument) | (quadratic_bezier_curveto_argument >> -comma-wsp >> quadratic_bezier_curveto_argument_sequence));
    quadratic_bezier_curveto = ((nsParser::lit (ABSOLUTE_QCURVETO) | nsParser::lit (RELATIVE_QCURVETO)) >> *wsp >> quadratic_bezier_curveto_argument_sequence);
    smooth_quadratic_bezier_curveto_argument_sequence = ((smooth_quadratic_bezier_curveto_argument) | (smooth_quadratic_bezier_curveto_argument >> -comma-wsp >> smooth_quadratic_bezier_curveto_argument_sequence));
    smooth_quadratic_bezier_curveto_argument = (coordinate_pair >> -comma_wsp >> smooth_quadratic_bezier_curveto_argument_sequence);
    smooth_quadratic_bezier_curveto = ((nsParser::lit (ABSOLUTE_SQCURVETO) | nsParser::lit (RELATIVE_SQCURVETO)) >> *wsp >> smooth_quadratic_bezier_curveto_argument_sequence);
    elliptical_arc_argument = nonnegative_number >> -comma_wsp >> nonnegative_number -comma_wsp >> number >> comma_wsp >> flag >> comma_wsp >> coordinate_pair;
    elliptical_arc_argument_sequence = (elliptical_arc_argument | (elliptical_arc_argument >> -comma_wsp >> elliptical_arc_argument_sequence));
    elliptical_arc = ((nsParser::lit (ABSOLUTE_ARCTO) | nsParser::lit (RELATIVE_ARCTO)) >> *wsp >> elliptical_arc_argument_sequence);

    drawto_command =
        closepath |
        lineto |
        horizontal_lineto |
        vertical_lineto |
        curveto |
        smooth_curveto |
        quadratic_bezier_curveto |
        smooth_quadratic_bezier_curveto |
        elliptical_arc;

    drawto_commands = (drawto_command | (drawto_command >> *wsp >> drawto_commands));
    moveto_drawto_command_group = (moveto >> *wsp >> -drawto_commands);
    moveto_drawto_command_groups = moveto_drawto_command_group | (moveto_drawto_command_group >> *wsp >> moveto_drawto_command_groups);

    svg_path = *wsp >> -moveto_drawto_command_groups >> *wsp;
}

</pre>
<pre>

NOTE : Obviously this is C++ rather than C, however I can’t seem to get WordPress to accept “C++” as a category name at present …

The shocking truth about floating point

Got a bit sidetracked again while writing some code to convert SVG paths to 6809 assembler suitable for a Vectrex (don’t ask), and ended up writing some code to convert numbers to words, with a view for adapting it at some point for TTS/PAF->grammar use.

Entertainingly, as I haven’t introduced any precision or rounding yet, entering – for instance  –  0.03f gives us what is actually stored in the float. You tend to forget that “0.03f” (at least on my PC) is actually more like “zero point zero two nine nine nine nine nine nine eight zero nine two six five one three six seven one eight seven five” …

Here’s the current, unoptimised, code. It requires a fairly recent version of Boost.

Snippet</pre>
<pre>#ifndef NUM_TO_WORDS_HPP_INCLUDED
#define NUM_TO_WORDS_HPP_INCLUDED

static std::string SpeakUnderOneHundred (uint16_t);
static std::string SpeakUnderOneThousand (uint16_t);

enum PrintScale
{
    Short,
    Long,
    LongWithMilliards
};

template <typename T> std::string numeric_to_words (T const & Input, PrintScale DisplayScale = Short, bool UseCommas=true)
{
    #if BOOST_VERSION >= 105500 
        BOOST_STATIC_ASSERT_MSG (boost::is_arithmetic<T>::value, "numeric_to_words can be only instantiated with arithmetic types");
    #else
        BOOST_STATIC_ASSERT (boost::is_arithmetic<T>::value);
    #endif

    const std::string ThousandsShortScale [] = {"", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion"};
    const std::string ThousandsLongScale [] = {"", "thousand", "million", "million", "billion", "billion", "trillion"};
    const std::string ThousandsLongScaleMB [] = {"", "thousand", "million", "milliard", "billion", "billiard", "trillion"};
    const uint8_t IsThousand = 40;  // 8 | 32
    
    const std::string * Thousands;

    const std::string single_digits [] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};

        const uint8_t MINIMUM_NOT_SET=0xFF;

    uint16_t PowerOf3Boxes [7];

    uint8_t MaxBox = 0, MinBox = MINIMUM_NOT_SET;

    std::string WriteAsString;

    uint8_t MagnitudeBlock = 7;

    uint64_t ValueAsInteger;

    T WorkingCopy = Input;
    
    switch (DisplayScale)
    {
        case Long : Thousands = &ThousandsLongScale [0]; break;
        case LongWithMilliards : Thousands = &ThousandsLongScaleMB [0]; break;
        default : Thousands = &ThousandsShortScale [0]; break;
    };

    #if BOOST_VERSION >= 104800
    if (boost::is_signed<T>::value)
    {
    #endif

        if  (Input < 0)
        {
            WriteAsString += "minus ";

            ValueAsInteger = (uint64_t) (0 - ((int64_t) Input));
        }
        else
            ValueAsInteger = (uint64_t) Input;
    #if BOOST_VERSION >= 104800
    }
    else
        ValueAsInteger = (uint64_t) Input;
    #endif

    if (ValueAsInteger)
    {
        WorkingCopy -= (T) ValueAsInteger;

        // Run forward to populate boxes and determine max/min.

        for (MaxBox = 0; MaxBox < 7; MaxBox++)
        {
        
            if ((PowerOf3Boxes [MaxBox] = (uint16_t) (ValueAsInteger - ((ValueAsInteger / 1000) * 1000))))
                if (MinBox == MINIMUM_NOT_SET) 
                    MinBox = MaxBox;

            ValueAsInteger -= PowerOf3Boxes [MaxBox];

            ValueAsInteger /= 1000;

            if (!ValueAsInteger) break;
        }

        MagnitudeBlock = MaxBox + 1;

        do
        {
            --MagnitudeBlock;

            if ((DisplayScale == Long) && ((1 << MagnitudeBlock) & IsThousand))
            {
                WriteAsString += numeric_to_words ((PowerOf3Boxes [MagnitudeBlock] * 1000) + PowerOf3Boxes [MagnitudeBlock - 1], Long, false);
                
                WriteAsString += " ";
                
                WriteAsString += Thousands [MagnitudeBlock];
                
                MagnitudeBlock --;
            }
            else
            {
                WriteAsString += SpeakUnderOneThousand (PowerOf3Boxes [MagnitudeBlock]);

                if (MagnitudeBlock) WriteAsString += " ";
                
                WriteAsString += Thousands [MagnitudeBlock];

            }

            
            if (MagnitudeBlock > MinBox)
            {
                if ((MagnitudeBlock == 1) && (PowerOf3Boxes [0] <= 99))
                    WriteAsString += " and";
                else if (UseCommas) WriteAsString += ",";
                
                WriteAsString += " ";
            }

        }
        while (MagnitudeBlock > MinBox);
    }
    else
    {
        // Zero

        WriteAsString += single_digits [0];
    }

    if (boost::is_float<T>::value)
    {
        if (WorkingCopy)
        {
            WriteAsString += " point";

            // Deal with decimal point.

            while (WorkingCopy)
            {
                WorkingCopy *= 10;

                WriteAsString += " " + single_digits [(int) WorkingCopy];

                WorkingCopy -= (int) WorkingCopy;
            }
        }
    }
    
    return WriteAsString;
}


static std::string SpeakUnderOneHundred (uint16_t DigitValue)
{
    const std::string Tens [] = {"", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};

    const std::string Teens [] = {"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"};

    const std::string SingleDigits [] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};

    std::string WriteAsString;

    if (DigitValue < 10)
        WriteAsString += SingleDigits [DigitValue];
    else if (DigitValue < 20)
        WriteAsString += Teens [DigitValue - 10];
    else
    {
        if (DigitValue / 10) WriteAsString += Tens [DigitValue / 10];
        
        if ((DigitValue / 10) && (DigitValue % 10)) WriteAsString += " ";
        
        if (DigitValue % 10) WriteAsString += SingleDigits [DigitValue % 10];
    }

    return WriteAsString;
}


static std::string SpeakUnderOneThousand (uint16_t DigitValue)
{
    int Hundreds;

    std::string WriteAsString;

    Hundreds = (int) DigitValue / 100;

    if (Hundreds)
    {
        WriteAsString += SpeakUnderOneHundred (Hundreds) + " hundred";

        DigitValue %= (Hundreds * 100);

        if (DigitValue) WriteAsString += " and ";				
    }

    if (DigitValue)	WriteAsString += SpeakUnderOneHundred (DigitValue);

    return WriteAsString;

}

#endif

</pre>
<pre>

And some unit tests, should that sort of thing make your boat floaty. You’ll need UnitTest++.

Snippet</pre>
<pre>#include <cstdlib>
#include <cmath>
#include <limits>
#include <string>
#include <iostream>
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/version.hpp>
 
#include <UnitTest++.h>
 
#if (__cplusplus >= 201103L)
    #include <cstdint>
#else
    #include <stdint.h>
#endif
 
using namespace std;
using namespace UnitTest;
 
#include "../numtowords.hpp"
 
int main (void);
 
TEST (ShortScale)
{
    CHECK_EQUAL ("one quintillion, two hundred and thirty four quadrillion, five hundred and sixty seven trillion, eight hundred and ninety billion, one hundred and twenty three million, four hundred and fifty six thousand, seven hundred and eighty nine", numeric_to_words (1234567890123456789LL));
    CHECK_EQUAL ("zero", numeric_to_words (0));
    CHECK_EQUAL ("one", numeric_to_words (1));
    CHECK_EQUAL ("twenty", numeric_to_words (20));
    CHECK_EQUAL ("one hundred and one", numeric_to_words (101));
    CHECK_EQUAL ("one hundred and twenty", numeric_to_words (120));
    CHECK_EQUAL ("zero", numeric_to_words (0.0));
    CHECK_EQUAL ("zero point one", numeric_to_words (0.1));
    CHECK_EQUAL ("minus ten", numeric_to_words (-10));
    CHECK_EQUAL ("one thousand and one", numeric_to_words (1001));
    CHECK_EQUAL ("one thousand, one hundred and one", numeric_to_words (1101));
    CHECK_EQUAL ("one thousand", numeric_to_words (1000.0f));
    CHECK_EQUAL ("one billion, two hundred and thirty four million, five hundred and sixty seven thousand, eight hundred and ninety", numeric_to_words (1234567890));
}
 
TEST (LongScale)
{
    CHECK_EQUAL ("zero", numeric_to_words (0, Long));
    CHECK_EQUAL ("one", numeric_to_words (1, Long));
    CHECK_EQUAL ("twenty", numeric_to_words (20, Long));
    CHECK_EQUAL ("one hundred and one", numeric_to_words (101, Long));
    CHECK_EQUAL ("one hundred and twenty", numeric_to_words (120, Long));
    CHECK_EQUAL ("zero", numeric_to_words (0.0, Long));
    CHECK_EQUAL ("zero point one", numeric_to_words (0.1, Long));
    CHECK_EQUAL ("minus ten", numeric_to_words (-10, Long));
    CHECK_EQUAL ("one thousand and one", numeric_to_words (1001, Long));
    CHECK_EQUAL ("one thousand", numeric_to_words (1000.0f, Long));
    CHECK_EQUAL ("one thousand two hundred and thirty four million, five hundred and sixty seven thousand, eight hundred and ninety", numeric_to_words (1234567890, Long));
    CHECK_EQUAL ("one trillion, two hundred and thirty four thousand five hundred and sixty seven billion, eight hundred and ninety thousand one hundred and twenty three million, four hundred and fifty six thousand, seven hundred and eighty nine", numeric_to_words (1234567890123456789LL, Long));
}
 
TEST (LongScaleWithMilliards)
{
    CHECK_EQUAL ("zero", numeric_to_words (0, LongWithMilliards));
    CHECK_EQUAL ("one", numeric_to_words (1, LongWithMilliards));
    CHECK_EQUAL ("twenty", numeric_to_words (20, LongWithMilliards));
    CHECK_EQUAL ("one hundred and one", numeric_to_words (101, LongWithMilliards));
    CHECK_EQUAL ("one hundred and twenty", numeric_to_words (120, LongWithMilliards));
    CHECK_EQUAL ("zero", numeric_to_words (0.0, LongWithMilliards));
    CHECK_EQUAL ("zero point one", numeric_to_words (0.1, LongWithMilliards));
    CHECK_EQUAL ("minus ten", numeric_to_words (-10, LongWithMilliards));
    CHECK_EQUAL ("one thousand and one", numeric_to_words (1001, LongWithMilliards));
    CHECK_EQUAL ("one thousand", numeric_to_words (1000.0f, LongWithMilliards));
    CHECK_EQUAL ("one milliard, two hundred and thirty four million, five hundred and sixty seven thousand, eight hundred and ninety", numeric_to_words (1234567890, LongWithMilliards));
    CHECK_EQUAL ("one trillion, two hundred and thirty four billiard, five hundred and sixty seven billion, eight hundred and ninety milliard, one hundred and twenty three million, four hundred and fifty six thousand, seven hundred and eighty nine", numeric_to_words (1234567890123456789LL, LongWithMilliards));
}
 
TEST (ShortScaleNoCommas)
{
    CHECK_EQUAL ("one billion two hundred and thirty four million five hundred and sixty seven thousand eight hundred and ninety", numeric_to_words (1234567890, Short, false));
    CHECK_EQUAL ("one quintillion two hundred and thirty four quadrillion five hundred and sixty seven trillion eight hundred and ninety billion one hundred and twenty three million four hundred and fifty six thousand seven hundred and eighty nine", numeric_to_words (1234567890123456789LL, Short, false));
}
 
TEST (LongScaleNoCommas)
{
    CHECK_EQUAL ("one thousand two hundred and thirty four million five hundred and sixty seven thousand eight hundred and ninety", numeric_to_words (1234567890, Long, false));
    CHECK_EQUAL ("one trillion two hundred and thirty four thousand five hundred and sixty seven billion eight hundred and ninety thousand one hundred and twenty three million four hundred and fifty six thousand seven hundred and eighty nine", numeric_to_words (1234567890123456789LL, Long, false));
}
 
TEST (LongScaleWithMilliardsNoCommas)
{
    CHECK_EQUAL ("one milliard two hundred and thirty four million five hundred and sixty seven thousand eight hundred and ninety", numeric_to_words (1234567890, LongWithMilliards, false));
    CHECK_EQUAL ("one trillion two hundred and thirty four billiard five hundred and sixty seven billion eight hundred and ninety milliard one hundred and twenty three million four hundred and fifty six thousand seven hundred and eighty nine", numeric_to_words (1234567890123456789LL, LongWithMilliards, false));
}
 
 
int main (void)
{	
    int count, ret = UnitTest::RunAllTests ();
 
    for (count = -9999; count < 9999; count++) numeric_to_words (count);
 
    return ret;
}
</pre>
<pre>

EDIT : Fixed code tags. Again.