June 11, 2004
That's more like it! Now, can the unit tests also test the case insensitivity?

"David L. Davis" <SpottedTiger@yahoo.com> wrote in message news:caa4h3$150f$1@digitaldaemon.com...
> In article <ca7qp4$hrl$1@digitaldaemon.com>, Walter says...
> >
> >There's no need to .dup the strings. Just have a loop that looks like
this:
> >
> >for (i = 0; i < string1.length; i++)
> >{    char c = toupper(string1[i]);
> >    if (c != toupper(string2[i]))
> >        goto nomatch;
> >}
> >
> >Note that it compares character by character without needing to allocate
> >memory. In fact, just copy the logic in find() and rfind(), replacing
memchr
> >and memcmp with case insensitive loops, write some unit tests, and you'll
be
> >there.
> >
> >
>
> Walter: Ok, per your advice I've copied the original find() / rfind()
functions
> from std.string, and modified them into ifind() / irfind(). I sure hope
these
> will make the grade <g>.
>
> Below I've used the "#" to keep the indenting in tact, so just replace the
"#" a
> "" and all the code will be ready to copy and paste.
>
> Thxs for giving this chance to add something to "D!" :)
>
> #// string.d
> #// Written by Walter Bright
> #// Copyright (c) 2001 Digital Mars
> #// All Rights Reserved
> #// www.digitalmars.com
> #
> #// String handling functions.
> #debug=string; // uncomment to turn on debugging printf's
> #
> #debug(string)
> #{
> #    import std.c.stdio; // for printf()
> #}
> #
> #import std.string;
> #
> #/******************************************
> # * Find first occurrance of c in string s.
> # * Return index in s where it is found.
> # * Return -1 if not found.
> # *
> # * (A case insensitive version of std.string.find)
> # */
> #
> #int ifind
> #(
> #    in char[] s,
> #    in char   c
> #)
> #{
> #    char c1 = c;
> #    char c2;
> #
> #    c1 = ( c1 >= '\x41' && c1 <= '\x5A' ? c1 + '\x20' : c1 );
> #
> #    for (int i = 0; i  < s.length; i++)
> #    {
> #        c2 = s[ i ];
> #        c2 = ( c2 >= '\x41' && c2 <= '\x5A' ? c2 + '\x20' : c2 );
> #        if ( c1 == c2 ) return i;
> #    }
> #
> #    return -1;
> #} // end ifind( in char[], in char )
> #
> #unittest
> #{
> #    debug(string) printf("string.ifind(char[],char).unittest\n");
> #
> #    int i;
> #
> #    i = ifind( null, cast(char)'a' );
> #    assert( i == -1 );
> #    i = ifind( "def", cast(char)'a' );
> #    assert( i == -1 );
> #    i = ifind( "abba", cast(char)'a' );
> #    assert( i == 0 );
> #    i = ifind( "def", cast(char)'f' );
> #    assert( i == 2 );
> #}
> #
> #/******************************************
> # * Find last occurrance of c in string s.
> # * Return index in s where it is found.
> # * Return -1 if not found.
> # *
> # * (A case insensitive version of std.string.rfind)
> # */
> #
> #int irfind
> #(
> #    in char[] s,
> #    in char   c
> #)
> #{
> #    char c1 = c;
> #    char c2;
> #
> #    c1 = ( c1 >= '\x41' && c1 <= '\x5A' ? c1 + '\x20' : c1 );
> #
> #    for (int i = s.length - 1; i >= 0; i--)
> #    {
> #        c2 = s[ i ];
> #        c2 = ( c2 >= '\x41' && c2 <= '\x5A' ? c2 + '\x20' : c2 );
> #
> #        //debug(string) printf("pos=%d, s=%.*s, c=%c, c1=%c, c2=%c\n", i,
s, c,
> c1, c2);
> #
> #        if ( c1 == c2 ) return i;
> #    }
> #
> #    return -1;
> #
> #} // irfind( in char[], in char )
> #
> #unittest
> #{
> #    debug(string) printf("string.irfind(char[],char).unittest\n");
> #
> #    int i;
> #
> #    i = irfind(null, cast(char)'a');
> #    assert(i == -1);
> #    i = irfind("def", cast(char)'a');
> #    assert(i == -1);
> #    i = irfind("abba", cast(char)'a');
> #    assert(i == 3);
> #    i = irfind("def", cast(char)'f');
> #    assert(i == 2);
> #}
> #
> #/*************************************
> # * Find first occurrance of sub[] in string s[].
> # * Return index in s[] where it is found.
> # * Return -1 if not found.
> # *
> # * (A case insensitive version of std.string.find)
> # */
> #
> #int ifind
> #(
> #    in char[] s,
> #    in char[] sub
> #)
> #    out ( result )
> #    {
> # if ( result == -1 )
> # {
> # }
> # else
> # {
> #     assert( 0 <= result && result < s.length - sub.length + 1 );
> # }
> #    }
> #    body
> #    {
> #        int  ip = -1;
> #
> # if ( sub.length == 0 || s.length == 0 || sub.length > s.length - 1 )
> #            return -1; // was return 0;
> #
> # if ( sub.length == 1 )
> # {
> #            return ifind( s, cast(char)sub[ 0 ] );
> # }
> # else
> # {
> #            ip = ifind( s, cast(char)sub[ 0 ] );
> #
> #            if ( ip == -1 || ( ip + sub.length > s.length - 1 ) )
> #                return -1;
> #            else
> #            {
> #                for ( int x = ip; x < ( s.length - sub.length ) + 1;
x++ )
> #                    if ( icmp( s[ x .. ( x + sub.length ) ], sub ) == 0 )
> return x;
> #            }
> # }
> #
> # return -1;
> #
> #    }
> #// end ifind( in char[] s, in char[] sub )
> #
> #unittest
> #{
> #    debug(string) printf("string.ifind(char[],char[]).unittest\n");
> #
> #    int i;
> #
> #    i = ifind(null, "a");
> #    assert(i == -1);
> #    i = ifind("def", "a");
> #    assert(i == -1);
> #    i = ifind("abba", "a");
> #    assert(i == 0);
> #    i = ifind("def", "f");
> #    assert(i == 2);
> #    i = ifind("dfefffg", "fff");
> #    assert(i == 3);
> #    i = ifind("dfeffgfff", "fff");
> #    assert(i == 6);
> #}
> #
> #/*************************************
> # * Find last occurrance of sub in string s.
> # * Return index in s where it is found.
> # * Return -1 if not found.
> # *
> # * (A case insensitive version of std.string.rfind)
> # */
> #
> #int irfind(char[] s, char[] sub)
> #    out (result)
> #    {
> # if (result == -1)
> # {
> # }
> # else
> # {
> #     assert(0 <= result && result < s.length - sub.length + 1);
> # }
> #    }
> #    body
> #    {
> #        int  ip = -1;
> #
> # if ( sub.length == 0 || s.length == 0 || sub.length > s.length - 1 )
> #            return -1; // was return 0;
> #
> # if ( sub.length == 1 )
> # {
> #            return irfind( s, cast(char)sub[ 0 ] );
> # }
> # else
> # {
> #            ip = irfind( s, cast(char)sub[ 0 ] );
> #
> #            //debug(string) printf("1) ip=%d\n", ip);
> #
> #            if ( ip == -1 )
> #                return -1;
> #            else
> #            {
> #                //debug(string) printf("2) ip=%d\n", ip);
> #
> #                for ( int x = ip; x >= 0; x-- )
> #                    if ( icmp( s[ x .. ( x + sub.length ) ], sub ) == 0 )
> return x;
> #            }
> # }
> #
> # return -1;
> #    }
> #// end irfind( in char[], in char[] )
> #
> #unittest
> #{
> #    debug(string) printf("string.rifind(char[],char[]).unittest\n");
> #
> #    int i;
> #
> #    i = irfind("abcdefcdef", "c");
> #    assert(i == 6);
> #    i = irfind("abcdefcdef", "cd");
> #    assert(i == 6);
> #    i = irfind("abcdefcdef", "x");
> #    assert(i == -1);
> #    i = irfind("abcdefcdef", "xy");
> #    assert(i == -1);
> #    i = irfind("abcdefcdef", "");
> #    assert(i == -1);
> #    i = irfind( "abcdefcdef", "def" );
> #    assert(i == 7);
> #}
> #
> #// Testing the above functions
> #int main()
> #{
> #    printf("string.ifind(char[],char).unittest\n");
> #
> #    printf("ifind( null, cast(char)'a') = %d, ans = -1\n", ifind( null,
> cast(char)'a' ) );
> #    printf("ifind( \"def\", cast(char)'a' ) = %d, ans = -1\n", ifind(
"def",
> cast(char)'a' ) );
> #    printf("ifind( \"abba\", cast(char)'a' ) = %d, ans = 0\n", ifind(
"abba",
> cast(char)'a' ) );
> #    printf("ifind( \"def\", cast(char)'f' ) = %d, ans = 2\n", ifind(
"def",
> cast(char)'f' ) );
> #
> #    printf("\n\n");
> #
> #    printf("string.irfind(char[],char).unittest\n");
> #    printf("irfind( null, cast(char)'a' ) = %d, ans = -1\n", irfind(
null,
> cast(char)'a' ) );
> #    printf("irfind( \"def\", cast(char)'a' ) = %d, ans= -1\n",irfind(
"def",
> cast(char)'a') );
> #    printf("irfind( \"abba\", cast(char)'a' ) = %d, ans= 3\n", irfind(
"abba",
> cast(char)'a') );
> #    printf("irfind( \"def\", cast(char)'f' ) = %d, ans = 2\n", irfind(
"def",
> cast(char)'f') );
> #
> #    printf("\n\n");
> #
> #    printf("string.ifind(char[],char[]).unittest\n");
> #    printf("ifind( null, \"a\" ) = %d, ans = -1\n", ifind( null, "a" ) );
> #    printf("ifind( \"def\", \"a\" ) = %d, ans = -1\n", ifind( "def",
"a" ) );
> #    printf("ifind( \"abba\", \"a\" ) = %d, ans= 0\n", ifind( "abba",
"a" ) );
> #    printf("ifind( \"def\", \"f\") = %d, ans = 2\n", ifind( "def",
"f" ) );
> #    printf("ifind( \"dfefffg\", \"fff\") = %d, ans = 3\n", ifind(
"dfefffg",
> "fff" ) );
> #    printf("ifind( \"dfeffgfff\", \"fff\") = %d, ans = 6\n", ifind(
> "dfeffgfff", "fff" ) );
> #
> #    printf("\n\n");
> #
> #    printf("string.rifind(char[],char[]).unittest\n");
> #    printf("irfind( \"abcdefcdef\", \"c\" ) = %d, ans = 6\n", irfind(
> "abcdefcdef", "c" ) );
> #    printf("irfind( \"abcdefcdef\", \"cd\" ) = %d, ans = 6\n", irfind(
> "abcdefcdef", "cd" ) );
> #    printf("irfind( \"abcdefcdef\", \"x\" ) = %d, ans= -1\n", irfind(
> "abcdefcdef", "x" ) );
> #    printf("irfind( \"abcdefcdef\", \"xy\") = %d, ans = -1\n", irfind(
> "abcdefcdef", "xy" ) );
> #    printf("irfind( \"abcdefcdef\", \"\") = %d, ans = -1\n", irfind(
> "abcdefcdef", "" ) );
> #    printf("irfind( \"abcdefcdef\", \"def\") = %d, ans = 7\n", irfind(
> "abcdefcdef", "def" ) );
> #
> #    return 0;
> #}
>
> -------------------------------------------------------------------
> "Dare to reach for the Stars...Dare to Dream, Build, and Achieve!"


June 11, 2004
"Hauke Duden" <H.NS.Duden@gmx.net> wrote in message news:ca9csf$2ujf$1@digitaldaemon.com...
> Arcane Jill wrote:
> > I think it would be great if a D standard library had FULL Unicode
support. Even
> > C++ and Java don't do that. (And that's not even mentioning Java's
crippled
> > 16-bit chars). It would effectively turn D into the language of choice
for
> > Unicode apps.
>
> I agree - that is my goal as well. In fact I see it as an opportunity to influence the language in its early stages so that it will have standardized(!) Unicode support. It prevents every component developer from implementing his own, which can cause lots of unnecessary bloat (Unicode data isn't small...).

I agree too, and am glad you two are taking the lead on it.


June 11, 2004
"Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:ca9if2$5ag$1@digitaldaemon.com...
> Now, what I'm about to say may possibly make me a little unpopular. I
/hope/
> not, but I wish to be accurate, and, well, if what can I say? Please don't
shoot
> the messenger! The fact is, /if/ Unicode has got it wrong, then the place
to
> complain about it is the Unicode Consortium public forum at http://www.unicode.org/consortium/distlist.html - NOT the D forum. Our job
is to
> implement the Unicode standard as it exists today at revision 4.1 - even
if we
> think that standard is wrong. It would be inappropriate for us to start
tweaking
> it here and there just because we don't like bits of it. Errors and
omissions in
> the standard are certainly possible (and even likely), but a standard is a standard, and such errors will inevitably be fixed in the course of time.
If and
> when the standard changes, that's when we should change with it.

My experience with implementing Standards is that the right way is to shut off one's brain and pedantically, exactly, implement it, right or wrong. All trying to fix bugs in the Standards does is cause "your implementation is different from the Standard, therefore you are wrong" bug reports. And to be frank, they're right.

I agree with you, Jill.


June 11, 2004
"Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:ca7ri6$iuo$1@digitaldaemon.com...
> In article <ca7qp5$hrl$2@digitaldaemon.com>, Walter says...
> >
> >Another option is to only allow bit slicing on byte boundaries, and only allow pointers to bits if they are in bit 0 of a byte.
> >
> >
>
> That's EXACTLY what my workaround does. You can have the code for free if
you
> want.

Thanks!


June 11, 2004
In article <cabhl0$7f2$2@digitaldaemon.com>, Walter says...
>
>> That's EXACTLY what my workaround does. You can have the code for free if
>you
>> want.
>
>Thanks!

Oky doke - here goes. One thing though - this is merely a workaround for existing bugs, it does not really add any new functionality beyond what such arrays are supposed to do already. So I don't imagine you will use this code. You'd probably prefer to just fix the bugs, then a workaround won't be needed at all.

>module etc.workaround.bitslice;
>
>/*    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    This is a workaround for the bug whereby:
>
>        // given a non-null bit[] b;
>        b[i..j]
>
>    references the wrong data, often causing an access violation.
>
>    Usage:    Replace                With
>            --------------------------------------------------
>            b[i..j]                bitSlice(b, i, j)
>            b[] = expr            bitSliceAssign(b, expr)
>            b[i..j] = expr;        bitSliceAssign(b, i, j, expr);
>            b ~ c                bitSliceCat(b, c);
>            b ~= c                bitSliceCatAssign(b, c);
>*/
>
>//===============================================
>// This version is for reading from a bit slice
>//
>unittest
>{
>    bit b[256];
>    b[0] = b[16] = 1;
>    confirm(bitSlice(b,0,16) == bitSlice(b,16,32));
>}
>
>bit[] bitSlice(bit[] b, uint i, uint j)
>{
>    version(BitSliceWorkaround)
>    {
>        if (((i | j) & 7) != 0) throw new BitSliceException("Can only slice by whole bytes");
>        BitSliceUnion u;
>
>        // Convert from a bit slice to a ubyte slice
>        u.bitRef = b;
>        assert((u.length & 7) == 0);
>        u.length >>= 3;
>
>        // Take the desired slice
>        u.ubyteRef = u.ubyteRef[i>>3..j>>3];
>
>        // Convert it back to a bit slice
>        u.length <<= 3;
>        return u.bitRef;
>    }
>    else
>    {
>        return b[i..j];    // Assumes bit slicing works. This will be unit tested.
>    }
>}
>
>//=================================================================== // This version is for writing to a bit slice with a constant value //
>
>unittest
>{
>    bit b[256];
>    bitSliceAssign(b,16,32,1);
>    confirm(b[16] == 1);
>}
>
>bit[] bitSliceAssign(bit[] b, bit e)
>{
>    return bitSliceAssign(b, 0, b.length, e);
>}
>
>bit[] bitSliceAssign(bit[] b, uint i, uint j, bit e)
>{
>    version(BitSliceWorkaround)
>    {
>        if (((i | j) & 7) != 0) throw new BitSliceException("Can only slice by whole bytes");
>        BitSliceUnion u;
>
>        // Convert from a bit slice to a ubyte slice
>        u.bitRef = b;
>        assert((u.length & 7) == 0);
>        u.length >>= 3;
>
>        // Write into the desired slice
>        u.ubyteRef[i>>3..j>>3] = (e ? 0xFF : 0);
>
>        // Convert back to a bit slice
>        u.length <<= 3;
>        return u.bitRef;
>    }
>    else
>    {
>        return b[i..j] = e;    // Assumes bit slicing works. This will be unit tested.
>    }
>}
>
>//========================================================= // This version is for pasting one bit slice into another //
>
>unittest
>{
>    bit b[256];
>    bit e[16];
>    e[0] = 1;
>    bitSliceAssign(b, 16, 32, bitSlice(e, 0, 16));
>    confirm(b[16] == 1);
>}
>
>bit[] bitSliceAssign(bit[] b, bit[] e)
>{
>    return bitSliceAssign(b, 0, b.length, e);
>}
>
>bit[] bitSliceAssign(bit[] b, uint i, uint j, bit[] e)
>in
>{
>    assert(j - i == e.length);
>}
>body
>{
>    version(BitSliceWorkaround)
>    {
>        if (((i | j) & 7) != 0) throw new BitSliceException("Can only slice by whole bytes");
>        BitSliceUnion ub, ue;
>
>        // Convert from bit slices to ubyte slices
>        ub.bitRef = b;
>        assert((ub.length & 7) == 0);
>        ub.length >>= 3;
>
>        ue.bitRef = e;
>        assert((ue.length & 7) == 0);
>        ue.length >>= 3;
>
>        // Write the desired slice
>        ub.ubyteRef[i>>3..j>>3] = ue.ubyteRef[0..(j-i)>>3];
>
>        // Convert everything back to bit slices
>        ub.length <<= 3;
>        ue.length <<= 3;
>        return ub.bitRef;
>    }
>    else
>    {
>        return b[i..j] = e[0..j-i];    // Assumes bit slicing works. This will be unit tested.
>    }
>}
>
>//============================================================= // This version is for concatenating one bit slice onto another //
>
>bit[] bitSliceCat(bit[] b, bit[] c)
>{
>    bit[] r;
>    r.length = b.length + c.length;
>    bitSliceAssign(r, 0, b.length, b);
>    bitSliceAssign(r, b.length, r.length, c);
>    return r;
>}
>
>//=============================================================
>// This version is for concatenating one bit slice onto another and assigning the result back
>// onto the original
>//
>
>bit[] bitSliceCatAssign(inout bit[] b, bit[] c)
>{
>    uint bLen = b.length;
>    b.length = bLen + c.length;
>    bitSliceAssign(b, bLen, b.length, c);
>    return b;
>}
>
>// Supporting stuff
>
>private union BitSliceUnion
>{
>    bit[]    bitRef;
>    ubyte[] ubyteRef;
>    uint    length;
>}
>
>class BitSliceException : Exception
>{
>    this(char[] s)
>    {
>        super(s);
>    }
>}
>
>void confirm(int assertion)
>{
>    debug
>    {
>        if (!assertion)
>        {
>            printf("This version of the D compiler contains a bug which prevents\n");
>            printf("slicing of bit arrays from working properly\n\n");
>            printf("You need to define the symbol BitSliceWorkaround, and replace\n");
>            printf("all bit array slicing operations with the appropriate function\n");
>            printf("from etc.workaround.bitslice\n");
>        }
>    }
>    assert(assertion);
>}
>


June 11, 2004
In article <cabh23$6m1$1@digitaldaemon.com>, Walter says...
>
>That's more like it! Now, can the unit tests also test the case insensitivity?
>

Walter: Opps! Sorry about that...I've now added in some additional unittest entrys for all four functions.

#// string.d
#// Written by Walter Bright
#// Copyright (c) 2001 Digital Mars
#// All Rights Reserved
#// www.digitalmars.com
#
#// String handling functions.
#debug=string;		// uncomment to turn on debugging printf's
#
#debug(string)
#{
#    import std.c.stdio;	// for printf()
#}
#
#import std.string;
#
#/******************************************
# * Find first occurrance of c in string s.
# * Return index in s where it is found.
# * Return -1 if not found.
# *
# * (A case insensitive version of std.string.find)
# */
#
#int ifind
#(
#    in char[] s,
#    in char   c
#)
#{
#    char c1 = c;
#    char c2;
#
#    c1 = ( c1 >= '\x41' && c1 <= '\x5A' ? c1 + '\x20' : c1 );
#
#    for (int i = 0; i  < s.length; i++)
#    {
#        c2 = s[ i ];
#        c2 = ( c2 >= '\x41' && c2 <= '\x5A' ? c2 + '\x20' : c2 );
#        if ( c1 == c2 ) return i;
#    }
#
#    return -1;
#
#} // end ifind( in char[], in char )
#
#unittest
#{
#    debug(string) printf("string.ifind(char[],char).unittest\n");
#
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    int i;
#
#    i = ifind(null, cast(char)'a');
#    assert(i == -1);
#    i = ifind("def", cast(char)'a');
#    assert(i == -1);
#    i = ifind("abba", cast(char)'a');
#    assert(i == 0);
#    i = ifind("def", cast(char)'f');
#    assert(i == 2);
#
#    i = ifind(sPlts, cast(char)'P');
#    assert(i == 23);
#    i = ifind(sPlts, cast(char)'R');
#    assert(i == 2);
#}
#
#/******************************************
# * Find last occurrance of c in string s.
# * Return index in s where it is found.
# * Return -1 if not found.
# *
# * (A case insensitive version of std.string.rfind)
# */
#
#int irfind
#(
#    in char[] s,
#    in char   c
#)
#{
#    char c1 = c;
#    char c2;
#
#    c1 = ( c1 >= '\x41' && c1 <= '\x5A' ? c1 + '\x20' : c1 );
#
#    for (int i = s.length - 1; i >= 0; i--)
#    {
#        c2 = s[ i ];
#        c2 = ( c2 >= '\x41' && c2 <= '\x5A' ? c2 + '\x20' : c2 );
#
#        //debug(string) printf("pos=%d, s=%.*s, c=%c, c1=%c, c2=%c\n", i, s, c,
c1, c2);
#
#        if ( c1 == c2 ) return i;
#    }
#
#    return -1;
#
#} // end irfind( in char[], in char )
#
#unittest
#{
#    debug(string) printf("string.irfind(char[],char).unittest\n");
#
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    int i;
#
#    i = irfind(null, cast(char)'a');
#    assert(i == -1);
#    i = irfind("def", cast(char)'a');
#    assert(i == -1);
#    i = irfind("abba", cast(char)'a');
#    assert(i == 3);
#    i = irfind("def", cast(char)'f');
#    assert(i == 2);
#
#    i = irfind(sPlts, cast(char)'M');
#    assert(i == 34);
#    i = irfind(sPlts, cast(char)'S');
#    assert(i == 40);
#}
#
#/*************************************
# * Find first occurrance of sub[] in string s[].
# * Return index in s[] where it is found.
# * Return -1 if not found.
# *
# * (A case insensitive version of std.string.find)
# */
#
#int ifind
#(
#    in char[] s,
#    in char[] sub
#)
#    out ( result )
#    {
#	if ( result == -1 )
#	{
#	}
#	else
#	{
#	    assert( 0 <= result && result < s.length - sub.length + 1 );
#	}
#    }
#    body
#    {
#        int  ip = -1;
#
#	if ( sub.length == 0 || s.length == 0 || sub.length > s.length - 1 )
#            return -1; // was return 0;
#
#	if ( sub.length == 1 )
#	{
#            return ifind( s, cast(char)sub[ 0 ] );
#	}
#	else
#	{
#            ip = ifind( s, cast(char)sub[ 0 ] );
#
#            if ( ip == -1 || ( ip + sub.length > s.length - 1 ) )
#                return -1;
#            else
#            {
#                for ( int x = ip; x < ( s.length - sub.length ) + 1; x++ )
#                    if ( icmp( s[ x .. ( x + sub.length ) ], sub ) == 0 )
return x;
#            }
#	}
#
#	return -1;
#
#    }
#// end ifind( in char[], in char[] )
#
#unittest
#{
#    debug(string) printf("string.ifind(char[],char[]).unittest\n");
#
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    char[] sMars = "Who\'s \'My Favorite Maritian?\'";
#    int i;
#
#    i = ifind(null, "a");
#    assert(i == -1);
#    i = ifind("def", "a");
#    assert(i == -1);
#    i = ifind("abba", "a");
#    assert(i == 0);
#    i = ifind("def", "f");
#    assert(i == 2);
#    i = ifind("dfefffg", "fff");
#    assert(i == 3);
#    i = ifind("dfeffgfff", "fff");
#    assert(i == 6);
#
#    i = ifind(sMars, "MY fAVe");
#    assert(i == -1);
#    i = ifind(sMars, "mY fAVOriTe");
#    assert(i == 7);
#    i = ifind(sPlts, "mArS:");
#    assert(i == 0);
#    i = ifind(sPlts, "rOcK");
#    assert(i == 17);
#    i = ifind(sPlts, "Un.");
#    assert(i == 41);
#}
#
#/*************************************
# * Find last occurrance of sub in string s.
# * Return index in s where it is found.
# * Return -1 if not found.
# *
# * (A case insensitive version of std.string.rfind)
# */
#
#int irfind(char[] s, char[] sub)
#    out (result)
#    {
#	if (result == -1)
#	{
#	}
#	else
#	{
#	    assert(0 <= result && result < s.length - sub.length + 1);
#	}
#    }
#    body
#    {
#        int  ip = -1;
#
#	if ( sub.length == 0 || s.length == 0 || sub.length > s.length - 1 )
#            return -1; // was return 0;
#
#	if ( sub.length == 1 )
#	{
#            return irfind( s, cast(char)sub[ 0 ] );
#	}
#	else
#	{
#            ip = irfind( s, cast(char)sub[ 0 ] );
#
#            //debug(string) printf("1) ip=%d\n", ip);
#
#            if ( ip == -1 )
#                return -1;
#            else
#            {
#                //debug(string) printf("2) ip=%d\n", ip);
#
#                for ( int x = ip; x >= 0; x-- )
#                    if ( icmp( s[ x .. ( x + sub.length ) ], sub ) == 0 )
return x;
#            }
#	}
#
#	return -1;
#    }
#// end irfind( in char[], in char[] )
#
#unittest
#{
#    debug(string) printf("string.irfind(char[],char[]).unittest\n");
#
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    char[] sMars = "Who\'s \'My Favorite Maritian?\'";
#    int i;
#
#    i = irfind("abcdefcdef", "c");
#    assert(i == 6);
#    i = irfind("abcdefcdef", "cd");
#    assert(i == 6);
#    i = irfind("abcdefcdef", "x");
#    assert(i == -1);
#    i = irfind("abcdefcdef", "xy");
#    assert(i == -1);
#    i = irfind("abcdefcdef", "");
#    assert(i == -1);
#    i = irfind( "abcdefcdef", "def" );
#    assert(i == 7);
#
#    i = irfind(sMars, "RiTE maR");
#    assert(i == 14);
#    i = irfind(sPlts, "FOuRTh");
#    assert(i == 10);
#    i = irfind(sMars, "whO\'s \'MY");
#    assert(i == 0);
#}
#
#// Smoke testing the above functions
#int main()
#{
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    char[] sMars = "Who\'s \'My Favorite Maritian?\'";
#
#    printf("string.ifind(char[],char).unittest\n");
#
#    printf("ifind( null, cast(char)'a') = %d, ans = -1\n",
#            ifind( null, cast(char)'a' ) );
#    printf("ifind( \"def\", cast(char)'a' ) = %d, ans = -1\n",
#            ifind( "def", cast(char)'a' ) );
#    printf("ifind( \"abba\", cast(char)'a' ) = %d, ans = 0\n",
#            ifind( "abba", cast(char)'a' ) );
#    printf("ifind( \"def\", cast(char)'f' ) = %d, ans = 2\n",
#            ifind( "def", cast(char)'f' ) );
#    printf("ifind( \"%.*s\", \'P\' ) = %d ans = 23\n", sPlts,
#            ifind( sPlts, cast(char)'P' ) );
#    printf("ifind( \"%.*s\", \'R\' ) = %d ans = 2\n", sPlts,
#            ifind( sPlts, cast(char)'R' ) );
#
#    printf("\n\n");
#
#    printf("string.irfind(char[],char).unittest\n");
#    printf("irfind( null, cast(char)'a' ) = %d, ans = -1\n",
#            irfind( null, cast(char)'a' ) );
#    printf("irfind( \"def\", cast(char)'a' ) = %d, ans= -1\n",
#            irfind( "def", cast(char)'a') );
#    printf("irfind( \"abba\", cast(char)'a' ) = %d, ans= 3\n",
#            irfind( "abba", cast(char)'a') );
#    printf("irfind( \"def\", cast(char)'f' ) = %d, ans = 2\n",
#            irfind( "def", cast(char)'f') );
#    printf("irfind( \"%.*s\", \'M\' ) = %d ans = 34\n", sPlts,
#            irfind( sPlts, cast(char)'M' ) );
#    printf("irfind( \"%.*s\", \'S\' ) = %d ans = 40\n", sPlts,
#            irfind( sPlts, cast(char)'S' ) );
#
#    printf("\n\n");
#
#    printf("string.ifind(char[],char[]).unittest\n");
#    printf("ifind( null, \"a\" ) = %d, ans = -1\n",
#            ifind( null, "a" ) );
#    printf("ifind( \"def\", \"a\" ) = %d, ans = -1\n",
#            ifind( "def", "a" ) );
#    printf("ifind( \"abba\", \"a\" ) = %d, ans= 0\n",
#            ifind( "abba", "a" ) );
#    printf("ifind( \"def\", \"f\") = %d, ans = 2\n",
#            ifind( "def", "f" ) );
#    printf("ifind( \"dfefffg\", \"fff\") = %d, ans = 3\n",
#            ifind( "dfefffg", "fff" ) );
#    printf("ifind( \"dfeffgfff\", \"fff\") = %d, ans = 6\n",
#            ifind( "dfeffgfff", "fff" ) );
#    printf("ifind( \"%.*s\", \"MY fAVe\" = %d ans = -1\n", sMars,
#            ifind( sMars, "MY fAVe" ) );
#    printf("ifind( \"%.*s\", \"mY fAVOriTe\" = %d ans = 7\n", sMars,
#            ifind( sMars, "mY fAVOriTe" ) );
#    printf("ifind( \"%.*s\", \"mArS:\" ) = %d ans = 0\n", sPlts,
#            ifind( sPlts, "mArS:" ) );
#    printf("ifind( \"%.*s\", \"rOcK\" ) = %d ans = 17\n", sPlts,
#            ifind( sPlts, "rOcK" ) );
#    printf("ifind( \"%.*s\", \"Un.\" ) = %d ans = 41\n", sPlts,
#            ifind( sPlts, "Un." ) );
#
#    printf("\n\n");
#
#    printf("string.irfind(char[],char[]).unittest\n");
#    printf("irfind( \"abcdefcdef\", \"c\" ) = %d, ans = 6\n",
#            irfind( "abcdefcdef", "c" ) );
#    printf("irfind( \"abcdefcdef\", \"cd\" ) = %d, ans = 6\n",
#            irfind( "abcdefcdef", "cd" ) );
#    printf("irfind( \"abcdefcdef\", \"x\" ) = %d, ans= -1\n",
#            irfind( "abcdefcdef", "x" ) );
#    printf("irfind( \"abcdefcdef\", \"xy\") = %d, ans = -1\n",
#            irfind( "abcdefcdef", "xy" ) );
#    printf("irfind( \"abcdefcdef\", \"\") = %d, ans = -1\n",
#            irfind( "abcdefcdef", "" ) );
#    printf("irfind( \"abcdefcdef\", \"def\") = %d, ans = 7\n",
#            irfind( "abcdefcdef", "def" ) );
#    printf("irfind( \"%.*s\", \"RiTE maR\" = %d ans = 14\n", sMars,
#            irfind( sMars, "RiTE maR" ) );
#    printf("irfind( \"%.*s\", \"FOuRTh\" ) = %d ans = 10\n", sPlts,
#            irfind( sPlts, "FOuRTh" ) );
#    printf("irfind( \"%.*s\", \"whO\'s \'MY\" = %d ans = 0\n", sMars,
#            irfind( sMars, "whO\'s \'MY" ) );
#
#    return 0;
#
#} // end int main()

-------------------------------------------------------------------
"Dare to reach for the Stars...Dare to Dream, Build, and Achieve!"
June 11, 2004
In article <cad0gf$29ad$1@digitaldaemon.com>, Arcane Jill says...
>
>In article <cabhl0$7f2$2@digitaldaemon.com>, Walter says...
>>
>>> That's EXACTLY what my workaround does. You can have the code for free if
>>you
>>> want.
>>
>>Thanks!
>
>Oky doke - here goes. One thing though - this is merely a workaround for existing bugs, it does not really add any new functionality beyond what such arrays are supposed to do already. So I don't imagine you will use this code. You'd probably prefer to just fix the bugs, then a workaround won't be needed at all.

Very nice.  This brings up a question though... assuming I want to know how many bits are in a byte, should I use the standard C defines or will D provide its own method?  I know it will probably be quite a long time before D is ported to a system that doesn't use 8-bit bytes, but I'm the careful type :)

Sean


June 12, 2004
Arcane Jill wrote:
> Anyway, let me know what you think (and check out UPR, if you have time. The URL
> is http://www.let.uu.nl/~Theo.Veenker/personal/projects/upr/, with the format
> itself documented at
> http://www.let.uu.nl/~Theo.Veenker/personal/projects/upr/format.html - or we
> could invent our own, but why re-invent the wheel?).

Ok, I finally got around to looking at it. It seems that UPR simply defines a binary representation that contains the normal Unicode data files in a more organized, easier-to-access way.

But the data does not seem to be compressed at all (please correct me if I have missed something). Also, each entry can be 1,2 or 4 bytes in size, but 3 bytes is actually the most size-efficient representation for uncompressed Unicode code points.

I fear that UPR doesn't quite cut it for data that is compiled statically into executables. After all, we don't want a "hello world" program to be several megabytes in size, right? That'd only cause people to ignore Unicode even more than they do now.

Hauke
June 12, 2004
In article <cabh23$6m1$1@digitaldaemon.com>, Walter says...
>
>That's more like it! Now, can the unit tests also test the case insensitivity?
>

Walter: Darn, I had to fix just one more thing in the code...I discovered if I the sub == the s I was giving an -1 instead of a 0. This newest version now fixes that.

Thxs!, for giving the chance to write these functions!! :))

#// string.d
#// Written by Walter Bright
#// Copyright (c) 2001 Digital Mars
#// All Rights Reserved
#// www.digitalmars.com
#
#// String handling functions.
#debug=string;		// uncomment to turn on debugging printf's
#
#debug(string)
#{
#    import std.c.stdio;	// for printf()
#}
#
#import std.string;
#
#/******************************************
# * Find first occurrance of c in string s.
# * Return index in s where it is found.
# * Return -1 if not found.
# *
# * (A case insensitive version of std.string.find)
# */
#
#int ifind
#(
#    in char[] s,
#    in char   c
#)
#{
#    char c1 = c;
#    char c2;
#
#    c1 = ( c1 >= '\x41' && c1 <= '\x5A' ? c1 + '\x20' : c1 );
#
#    for (int i = 0; i  < s.length; i++)
#    {
#        c2 = s[ i ];
#        c2 = ( c2 >= '\x41' && c2 <= '\x5A' ? c2 + '\x20' : c2 );
#        if ( c1 == c2 ) return i;
#    }
#
#    return -1;
#
#} // end ifind( in char[], in char )
#
#unittest
#{
#    debug(string) printf("string.ifind(char[],char).unittest\n");
#
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    int i;
#
#    i = ifind(null, cast(char)'a');
#    assert(i == -1);
#    i = ifind("def", cast(char)'a');
#    assert(i == -1);
#    i = ifind("abba", cast(char)'a');
#    assert(i == 0);
#    i = ifind("def", cast(char)'f');
#    assert(i == 2);
#
#    i = ifind(sPlts, cast(char)'P');
#    assert(i == 23);
#    i = ifind(sPlts, cast(char)'R');
#    assert(i == 2);
#}
#
#/******************************************
# * Find last occurrance of c in string s.
# * Return index in s where it is found.
# * Return -1 if not found.
# *
# * (A case insensitive version of std.string.rfind)
# */
#
#int irfind
#(
#    in char[] s,
#    in char   c
#)
#{
#    char c1 = c;
#    char c2;
#
#    c1 = ( c1 >= '\x41' && c1 <= '\x5A' ? c1 + '\x20' : c1 );
#
#    for (int i = s.length - 1; i >= 0; i--)
#    {
#        c2 = s[ i ];
#        c2 = ( c2 >= '\x41' && c2 <= '\x5A' ? c2 + '\x20' : c2 );
#
#        //debug(string) printf("pos=%d, s=%.*s, c=%c, c1=%c, c2=%c\n", i, s, c,
c1, c2);
#
#        if ( c1 == c2 ) return i;
#    }
#
#    return -1;
#
#} // end irfind( in char[], in char )
#
#unittest
#{
#    debug(string) printf("string.irfind(char[],char).unittest\n");
#
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    int i;
#
#    i = irfind(null, cast(char)'a');
#    assert(i == -1);
#    i = irfind("def", cast(char)'a');
#    assert(i == -1);
#    i = irfind("abba", cast(char)'a');
#    assert(i == 3);
#    i = irfind("def", cast(char)'f');
#    assert(i == 2);
#
#    i = irfind(sPlts, cast(char)'M');
#    assert(i == 34);
#    i = irfind(sPlts, cast(char)'S');
#    assert(i == 40);
#}
#
#/*************************************
# * Find first occurrance of sub[] in string s[].
# * Return index in s[] where it is found.
# * Return -1 if not found.
# *
# * (A case insensitive version of std.string.find)
# */
#
#int ifind
#(
#    in char[] s,
#    in char[] sub
#)
#    out ( result )
#    {
#	if ( result == -1 )
#	{
#	}
#	else
#	{
#	    assert( 0 <= result && result < s.length - sub.length + 1 );
#	}
#    }
#    body
#    {
#        int  ip = -1;
#
#	if ( sub.length == 0 || s.length == 0 || sub.length > s.length )
#            return -1; // was return 0;
#
#	if ( sub.length == 1 )
#	{
#            return ifind( s, cast(char)sub[ 0 ] );
#	}
#	else
#	{
#            ip = ifind( s, cast(char)sub[ 0 ] );
#
#            if ( ip == -1 || ( ip + sub.length > s.length ) )
#                return -1;
#            else
#            {
#                for ( int x = ip; x < ( s.length - sub.length ) + 1; x++ )
#                    if ( icmp( s[ x .. ( x + sub.length ) ], sub ) == 0 )
return x;
#            }
#	}
#
#	return -1;
#
#    }
#// end ifind( in char[], in char[] )
#
#unittest
#{
#    debug(string) printf("string.ifind(char[],char[]).unittest\n");
#
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    char[] sMars = "Who\'s \'My Favorite Maritian?\'";
#    int i;
#
#    i = ifind(null, "a");
#    assert(i == -1);
#    i = ifind("def", "a");
#    assert(i == -1);
#    i = ifind("abba", "a");
#    assert(i == 0);
#    i = ifind("def", "f");
#    assert(i == 2);
#    i = ifind("dfefffg", "fff");
#    assert(i == 3);
#    i = ifind("dfeffgfff", "fff");
#    assert(i == 6);
#
#    i = ifind(sMars, "MY fAVe");
#    assert(i == -1);
#    i = ifind(sMars, "mY fAVOriTe");
#    assert(i == 7);
#    i = ifind(sPlts, "mArS:");
#    assert(i == 0);
#    i = ifind(sPlts, "rOcK");
#    assert(i == 17);
#    i = ifind(sPlts, "Un.");
#    assert(i == 41);
#    i = ifind(sPlts, sPlts);
#    assert(i == 0);
#}
#
#/*************************************
# * Find last occurrance of sub in string s.
# * Return index in s where it is found.
# * Return -1 if not found.
# *
# * (A case insensitive version of std.string.rfind)
# */
#
#int irfind
#(
#    in char[] s,
#    in char[] sub
#)
#    out (result)
#    {
#	if (result == -1)
#	{
#	}
#	else
#	{
#	    assert(0 <= result && result < s.length - sub.length + 1);
#	}
#    }
#    body
#    {
#        int  ip = -1;
#
#	if ( sub.length == 0 || s.length == 0 || sub.length > s.length )
#            return -1; // was return 0;
#
#	if ( sub.length == 1 )
#	{
#            return irfind( s, cast(char)sub[ 0 ] );
#	}
#	else
#	{
#            ip = irfind( s, cast(char)sub[ 0 ] );
#
#            //debug(string) printf("1) ip=%d\n", ip);
#
#            if ( ip == -1 )
#                return -1;
#            else
#            {
#                //debug(string) printf("2) ip=%d\n", ip);
#
#                for ( int x = ip; x >= 0; x-- )
#                    if ( icmp( s[ x .. ( x + sub.length ) ], sub ) == 0 )
return x;
#            }
#	}
#
#	return -1;
#    }
#// end irfind( in char[], in char[] )
#
#unittest
#{
#    debug(string) printf("string.irfind(char[],char[]).unittest\n");
#
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    char[] sMars = "Who\'s \'My Favorite Maritian?\'";
#    int i;
#
#    i = irfind("abcdefcdef", "c");
#    assert(i == 6);
#    i = irfind("abcdefcdef", "cd");
#    assert(i == 6);
#    i = irfind("abcdefcdef", "x");
#    assert(i == -1);
#    i = irfind("abcdefcdef", "xy");
#    assert(i == -1);
#    i = irfind("abcdefcdef", "");
#    assert(i == -1);
#    i = irfind( "abcdefcdef", "def" );
#    assert(i == 7);
#
#    i = irfind(sMars, "RiTE maR");
#    assert(i == 14);
#    i = irfind(sPlts, "FOuRTh");
#    assert(i == 10);
#    i = irfind(sMars, "whO\'s \'MY");
#    assert(i == 0);
#    i = irfind(sMars, sMars);
#    assert(i == 0);
#}
#
#// Smoke testing the above functions
#int main()
#{
#    char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
#    char[] sMars = "Who\'s \'My Favorite Maritian?\'";
#
#    printf("string.ifind(char[],char).unittest\n");
#
#    printf("ifind( null, cast(char)'a') = %d, ans = -1\n",
#            ifind( null, cast(char)'a' ) );
#    printf("ifind( \"def\", cast(char)'a' ) = %d, ans = -1\n",
#            ifind( "def", cast(char)'a' ) );
#    printf("ifind( \"abba\", cast(char)'a' ) = %d, ans = 0\n",
#            ifind( "abba", cast(char)'a' ) );
#    printf("ifind( \"def\", cast(char)'f' ) = %d, ans = 2\n",
#            ifind( "def", cast(char)'f' ) );
#    printf("ifind( \"%.*s\", \'P\' ) = %d ans = 23\n", sPlts,
#            ifind( sPlts, cast(char)'P' ) );
#    printf("ifind( \"%.*s\", \'R\' ) = %d ans = 2\n", sPlts,
#            ifind( sPlts, cast(char)'R' ) );
#
#    printf("\n\n");
#
#    printf("string.irfind(char[],char).unittest\n");
#    printf("irfind( null, cast(char)'a' ) = %d, ans = -1\n",
#            irfind( null, cast(char)'a' ) );
#    printf("irfind( \"def\", cast(char)'a' ) = %d, ans= -1\n",
#            irfind( "def", cast(char)'a') );
#    printf("irfind( \"abba\", cast(char)'a' ) = %d, ans= 3\n",
#            irfind( "abba", cast(char)'a') );
#    printf("irfind( \"def\", cast(char)'f' ) = %d, ans = 2\n",
#            irfind( "def", cast(char)'f') );
#    printf("irfind( \"%.*s\", \'M\' ) = %d ans = 34\n", sPlts,
#            irfind( sPlts, cast(char)'M' ) );
#    printf("irfind( \"%.*s\", \'S\' ) = %d ans = 40\n", sPlts,
#            irfind( sPlts, cast(char)'S' ) );
#
#    printf("\n\n");
#
#    printf("string.ifind(char[],char[]).unittest\n");
#    printf("ifind( null, \"a\" ) = %d, ans = -1\n",
#            ifind( null, "a" ) );
#    printf("ifind( \"def\", \"a\" ) = %d, ans = -1\n",
#            ifind( "def", "a" ) );
#    printf("ifind( \"abba\", \"a\" ) = %d, ans= 0\n",
#            ifind( "abba", "a" ) );
#    printf("ifind( \"def\", \"f\") = %d, ans = 2\n",
#            ifind( "def", "f" ) );
#    printf("ifind( \"dfefffg\", \"fff\") = %d, ans = 3\n",
#            ifind( "dfefffg", "fff" ) );
#    printf("ifind( \"dfeffgfff\", \"fff\") = %d, ans = 6\n",
#            ifind( "dfeffgfff", "fff" ) );
#    printf("ifind( \"%.*s\", \"MY fAVe\" = %d ans = -1\n", sMars,
#            ifind( sMars, "MY fAVe" ) );
#    printf("ifind( \"%.*s\", \"mY fAVOriTe\" = %d ans = 7\n", sMars,
#            ifind( sMars, "mY fAVOriTe" ) );
#    printf("ifind( \"%.*s\", \"mArS:\" ) = %d ans = 0\n", sPlts,
#            ifind( sPlts, "mArS:" ) );
#    printf("ifind( \"%.*s\", \"rOcK\" ) = %d ans = 17\n", sPlts,
#            ifind( sPlts, "rOcK" ) );
#    printf("ifind( \"%.*s\", \"Un.\" ) = %d ans = 41\n", sPlts,
#            ifind( sPlts, "Un." ) );
#    printf("ifind( \"%.*s\", \"%.*s\" ) = %d ans = 0\n", sPlts, sPlts,
#            ifind( sMars, sMars ) );
#
#    printf("\n\n");
#
#    printf("string.irfind(char[],char[]).unittest\n");
#    printf("irfind( \"abcdefcdef\", \"c\" ) = %d, ans = 6\n",
#            irfind( "abcdefcdef", "c" ) );
#    printf("irfind( \"abcdefcdef\", \"cd\" ) = %d, ans = 6\n",
#            irfind( "abcdefcdef", "cd" ) );
#    printf("irfind( \"abcdefcdef\", \"x\" ) = %d, ans= -1\n",
#            irfind( "abcdefcdef", "x" ) );
#    printf("irfind( \"abcdefcdef\", \"xy\") = %d, ans = -1\n",
#            irfind( "abcdefcdef", "xy" ) );
#    printf("irfind( \"abcdefcdef\", \"\") = %d, ans = -1\n",
#            irfind( "abcdefcdef", "" ) );
#    printf("irfind( \"abcdefcdef\", \"def\") = %d, ans = 7\n",
#            irfind( "abcdefcdef", "def" ) );
#    printf("irfind( \"%.*s\", \"RiTE maR\" = %d ans = 14\n", sMars,
#            irfind( sMars, "RiTE maR" ) );
#    printf("irfind( \"%.*s\", \"FOuRTh\" ) = %d ans = 10\n", sPlts,
#            irfind( sPlts, "FOuRTh" ) );
#    printf("irfind( \"%.*s\", \"whO\'s \'MY\" = %d ans = 0\n", sMars,
#            irfind( sMars, "whO\'s \'MY" ) );
#    printf("irfind( \"%.*s\", \"%.*s\" ) = %d ans = 0\n", sMars, sMars,
#            irfind( sMars, sMars ) );
#
#    return 0;
#
#} // end int main()

-------------------------------------------------------------------
"Dare to reach for the Stars...Dare to Dream, Build, and Achieve!"
June 13, 2004
In article <caep38$1vfr$1@digitaldaemon.com>, Hauke Duden says...
>
>Ok, I finally got around to looking at it. It seems that UPR simply defines a binary representation that contains the normal Unicode data files in a more organized, easier-to-access way.

Yeah, I stand corrected. The format isn't useful to us. I thought it would be, from reading the blurb, but it's just as easy for us to ignore it. I say we forget UPR then.

I've got some ideas, but it's too early in the morning for me right now. Will get back to you later when I've woken up a bit.

Jill