Like other things posted here without notices to the contrary, this
code is in the public domain.

This program depends on a recent Python and PyQt.

I was experimenting with incremental full-text filesystem search.
This program is probably a little too clever about caching, and
clearly not clever enough about actual text string searching.

There's some stuff in there about storing outline structures in text
files (or at least retrieving them from text files) which isn't used
at all.

I wrote this in November.

fsgraph.py:
#!/usr/bin/python
from __future__ import generators
import os, qd, subgraph
# provide a graphnot-style interface to a Unix filesystem
# (treating file contents as lists of lines)
def _walkfs(topdir):
    for filename in os.listdir(topdir):
        completepath = os.path.join(topdir, filename)
        yield filename, walkfs(completepath), completepath

def walkfs(topdir):
    if os.path.isdir(topdir): return subgraph.LazyList(_walkfs(topdir))
    else: return subgraph.LazyList(filecontents(topdir))

def filecontents(path):
    ii = 0
    for line in file(path):
        yield qd.chomp(qd.untabify(line)), [], (path, ii)
        ii += 1


subgraph.py:
#!/usr/bin/python
from __future__ import generators
import sys
sys.path.append('/home/kragen/devel')
import graphnot

# LazyList: a layer over the top of an iterator that remembers its previous
# values.  Importantly, allows you to determine whether it's going to return
# anything without throwing away any of its results.
class LazyList:
    def __init__(self, seq): self.seq, self.items = iter(seq), []
    def __getitem__(self, index):
        try:
            while len(self.items) <= index: self.items.append(self.seq.next())
        except StopIteration:
            self.seq = iter([])
            raise IndexError("list index out of range")
        return self.items[index]
    def __nonzero__(self):
        try: self[0]
        except IndexError: return 0
        else: return 1


def subgraph(graph, searchstr):
    for label, child, extra in graph:
        foundkids = subgraph(child, searchstr)
        if label.find(searchstr) != -1: yield label, foundkids, extra
        else:
            # generators are true even if empty, but we need to see if
            # foundkids is empty, but without throwing away its contents.
            # Listifying is too eager, though.
            # Too bad this doesn't work.
            foundkids = LazyList(foundkids)
            if foundkids: yield label, foundkids, extra

def _listify(graph):
    for label, child, extra in graph: yield label, listify(child), extra
def listify(graph): return list(_listify(graph))

def ok(a, b): assert a == b, (a, b)
def oklsg(graph, searchstr, result):
    ok(listify(subgraph(graph, searchstr)), listify(result))
def test():
    def test_lazylist():
        LL = LazyList([1, 2, 3])
        if LL: x = 'true'
        else: x = 'false'
        ok(x, 'true')
        ok(list(LL), [1, 2, 3])
        ok(list(LL), [1, 2, 3])
        LL = LazyList([])
        if LL: x = 'true'
        else: x = 'false'
        ok(x, 'false')
    test_lazylist()
    ok(listify([('a', [], [])]), [('a', [], [])])
    a, b = ([('a', (), [])],
            [('a', [], [])])
    assert a != b
    ok(listify(a), b)
    oklsg([], 'foo', [])
    oklsg([('bar', [], 'blort')], 'foo', [])
    oklsg([('foo', [], 'blort')], 'foo', [('foo', [], 'blort')])
    oklsg([('xfoox', [], [])], 'foo',
          [('xfoox', [], [])])
    oklsg([('xbarx', [('xfoox', [], [])], 'blort')], 'foo',
          [('xbarx', [('xfoox', [], [])], 'blort')])
    oklsg([('xbarx', [], []), ('xfoox', [], [])], 'foo',
          [('xfoox', [], [])])
    oklsg([('xbarx', [('xbarx', [], []), ('xfoox', [], [])], [])], 'foo',
          [('xbarx', [('xfoox', [], [])], [])])
    oklsg([('xfoox', [('xbarx', [], []), ('xfoox', [], [])], [])], 'foo',
          [('xfoox', [('xfoox', [], [])], [])])
    oklsg([('xfoox', [('xbarx', [], []), ('xfoox', [], [])], [])], 'bar',
          [('xfoox', [('xbarx', [], [])], [])])

    # verify laziness
    class Poison(Exception): pass
    def poisonseq(seq):
        for item in seq: yield item
        raise Poison
    def assert_raises(thunk, exc):
        try: thunk()
        except exc: pass
        else: raise AssertionError("Didn't raise", thunk, exc)

    # verify LazyList doesn't read past its first item
    ok(not not LazyList(poisonseq([1])), 1)

    # instantiating a subgraph doesn't do any iteration
    x = subgraph(poisonseq([]), 'foo')
    # but listifying it does
    assert_raises(lambda: list(x), Poison)
    # if you find the string in the edge label, you don't need to look
    # at the kids to return the edge
    ok(subgraph([('foo', poisonseq([]), [])], 'foo').next()[0], 'foo')
    # but if you don't find the string there, you have to look at some kids
    assert_raises(lambda: subgraph([('spo', poisonseq([]), [])], 'foo').next(),
                  Poison)
    # on the other hand, you also don't need to search all the edges on the
    # top level to find the first one to return
    x = subgraph(poisonseq([('foo', [], []),
                            ('bar', [], [])]), 'bar')
    ok(x.next()[0], 'bar')
    # but if you keep looking, you may need to look through all of them
    assert_raises(lambda: x.next(), Poison)
    # similarly, you don't need to look through all the edges on a deeper level
    # to find out that you should return the top level
    x = subgraph([('foo', poisonseq([('bar', [], [])]), [])], 'bar')
    item = x.next()
    ok(item[0], 'foo')
    # but if you insist on looking through the kids you'll have to look at all
    # of them
    assert_raises(lambda: listify([item]), Poison)

    # misc stress test
    stresstest()

def stresstest():
    expected = [
    ("The colons are part of the syntax.  So if you want colons, you have to do this", 
[], []),
("Quotes can extend a string across multiple lines", [], []),
('Practical applications might include', [
    ('a more powerful search utility that allows you to include extra edges', [
     ("for example, you might want to see all the email messages with a term",
      [('you might also want to exclude some edges', [
        ("""For example, if you're viewing a program as a graph, you might
want to exclude edges indicating references to a routine or 
variable.""", [], []),
        ], []),
       ], []),
     ], []),
    ("path-expressions and more powerful query languages", [], []),
    ], []),
("We'll need adaptors that allow you to read other file formats", [
    ("mailbox, of course", [], []),
    ], []),
]
    oklsg(graphnot.samplegraph, 'x', expected)
                            
test()


graphnot.py:
#!/usr/bin/python
# very unfinished.  graph-reading code.
# The idea is that nodes are iterable collections of edges; each edge
# has a textual label, a node that it points to, and other metadata
# that explains how it was stored in the file (e.g. how it was quoted.)

samplegraph = [
    ("""I want a serialization format for string-edge-labeled graphs like
those from UnQL, ideally a format mostly suitable for human
reading. I'm going to start with a serialization format for
edge-labeled trees, then add backreferences to make it general.""", [], []),
("A string with no colons, quotes, newlines, or leading or trailing whitespace.", [], 
[]),
("Another such string.  Both happen to end with periods.", [], []),
("This string labels an edge with children",
 [("Another string with no leading whitespace.", [], []),
  ("And another.", [], [])], []),
("The colons are part of the syntax.  So if you want colons, you have to do this",
 [("Put your strings in \" quotes, using \\ to escape quotes inside.", [], []),
  ("The following are part of this string: lemons and oranges.", [], [])], []),
("You can write more than one edge per line", [
    ("From", [("you", [], [])], []),
    ("To", [("me", [], [])], []),
    ], [], ),
("This is the same as this", [
    ("From", [("you", [], [])], []),
    ("To", [("me", [], [])], []),
    ], [], ),
("And you can move it onto the end", [
    ("From", [("you", [], [])], []),
    ("To", [("me", [], [])], []),
    ], [], ),
("The amount of whitespace on subsequent lines determines indent level.",
 [], []),
("Therefore, it determines which node you're starting from.", [], []),
("Quotes can extend a string across multiple lines", [
    ("""The first line of a quoted string can include leading whitespace.
Subsequent lines must be aligned either with the first line,
or with the open quote.  Leading spaces necessary to indent up to that
point are not included in the string, but further indentation there 
constitutes leading whitespace on those lines.  But trailing whitespace
is difficult, because it's not easily visible.  In order to allow encoding
it, you can backslash newlines within the string, and both the newline and
any whitespace before the newline is preserved.""", [], []),
    ], []),
("I'm not sure about the backreferences yet.", [], []),
("As described so far,", [], []),
("the only holes in the scheme", [], []),
("come from incorrect indentation and backslashes in quotes.", [], []),
('', [], []),
('I think sequence of children is important, as in XML.', [], []),
('', [], []),
('Practical applications might include', [
    ('an outline viewer', [], []),
    ('a search utility that', [
     ('prunes trees to include only the edges necessary to reach search terms',
      [], []),
     ('displays them in outline view', [], []),
     ('searches incrementally', [], []),
     ], []),
    ('a more powerful search utility that allows you to include extra edges', [
     ("for example, you might want to see all the email messages with a term",
      [
       ("but you probably want to see the From:, To:, and Subject: too",
        [], []),
       ('you might also want to exclude some edges', [
        ("""For example, if you're viewing a program as a graph, you might
want to exclude edges indicating references to a routine or 
variable.""", [], []),
        ], []),
       ], []),
     ], []),
    ("path-expressions and more powerful query languages", [], []),
    ("a report engine to display tabular data", [], []),
    ("an editor (perhaps an Emacs mode) --- for outlines of course", [], []),
    ("a data-entry system", [], []),
    ], []),
("", [], []),
("We'll need adaptors that allow you to read other file formats", [
    ("mailbox, of course", [], []),
    ("merely a greppable file; one line per string, no hierarchy", [], []),
    ("a directory tree of files", [], []),
    ("and all lazily, naturally, so you can view large files", [], []),
    ("SQL databases, perhaps, although probably not while I'm at AirWave",
     [], []),
    ("source code", [], []),
    ], []),
]

def canonreadlabel(thefile, pos):
    # the if is a dubious efficiency hack
    if thefile.tell() != pos: thefile.seek(pos)
    nextch = thefile.read(1)
    assert nextch == '"', `nextch`
    labelchars = []
    while 1:
        labelchars.append(thefile.read(1))
        if labelchars[-1] == '"':
            labelchars.pop()
            return (''.join(labelchars), thefile.tell(), -31337)

class canonreaderkids:
    def __init__(self, thefile, pos, indent):
        self.thefile, self.pos, self.indent = thefile, pos, indent
    def __iter__(self): return self
    def next(self):
        label, newpos, indent = canonreadlabel(self.thefile, self.pos)
        return (label,
                canonreader(self.thefile, newpos, indent),
                [])

class canonreader:
    def __init__(self, thefile, pos=0, indent=''):
        self.thefile, self.pos, self.indent = thefile, pos, indent
    def __iter__(self):
        return canonreaderkids(self.thefile, self.pos, self.indent)

def test():
    graphnotfile = file('graph-notation')
    graphnotrootnode = canonreader(graphnotfile)
    kids = iter(graphnotrootnode)
    firstlabel, firstkid, otherinfo = kids.next()
    assert firstlabel.startswith("I want a serialization format")
    assert list(firstkid) == []
    kids2 = iter(graphnotrootnode)
    assert kids2.next()[0].startswith("I want a serialization format")
    assert kids.next()[0] == ""
    assert kids.next()[0].startswith("A string with no colons")
    kids2.next()
    assert kids2.next()[0].startswith("A string with no colons")

if __name__ == '__main__': test()


fsgraph.py tries to import qd ("quick-and-dirty").  Here's qd.py:
def chomp(text):
    while text[-1:] in ['\r', '\n']: text = text[:-1]
    return text

assert chomp('a\n') == 'a'
assert chomp('\n') == ''
assert chomp('') == ''

def untabify(text):
    def dumb_tab_exp(char):
        if char != '\t': return char
        return '    '
    return ''.join(map(dumb_tab_exp, text))



Finally, here's the main program:
#!/usr/bin/python
import qt, sys, subgraph, fsgraph
# interactive incremental grep -r substitute.  Works on files, better on
# directories.

# TODO:
# - better feedback on when search is complete
# - show partial results while searching; generally be responsive while
#   searching
# - search more efficiently for long strings --- it's obviously somewhat
#   advantageous to skip over the items you've already decided don't contain
#   the largest proper prefix of the string, as we do, but once we get beyond
#   the stuff we've already looked at, it's stupid to go through N levels
#   of filtering.
# - don't remember everything forever in LazyLists.  Throw things away!
# - navigation control: in and out, up and down.  especially "in",
#   followed by loosening up the search string
# - read outlines from actual files (as opposed to viewing
#   the filesystem as an outline)
# - decorate: with quotes, colons, backslashes
# - provide expand and collapse.  Buttons?  Doesn't Qt have some kind of
#   outline widget?
# - alpha blending would be great.  QImage and QPixmap have alpha masks.
#   QColor has an alpha.

class outlineview(qt.QWidget):
    def __init__(self, parent, target):
        qt.QWidget.__init__(self, parent, None,
                            qt.Qt.WStaticContents | qt.Qt.WRepaintNoErase)
        self.target = target
        self.fontSize = 8
        self.searchtext = ''
        self.setEraseColor(self.white)
        self.setFocusPolicy(qt.QWidget.StrongFocus)
        self.resetOutline()
        self.setFont()
        self.redrawTimer = qt.QTimer(self, 'redraw timer')
        self.connect(self.redrawTimer, qt.SIGNAL('timeout()'),
                     self.redraw)
    def setFont(self, delta=0):
        self.fontSize += delta
        self.font = qt.QFont('Urw Palladio l', self.fontSize)
        self.update()
    def resetOutline(self):
        self.outlines = [fsgraph.walkfs(self.target)]
    def keyPressEvent(self, ev):
        a, b = len(self.outlines), len(self.searchtext) + 1
        assert a == b, (a, b)
        if ev.key() == qt.Qt.Key_Backspace:
            if self.searchtext:
                self.searchtext = self.searchtext[:-1]
                self.outlines.pop()
        elif ev.key() in (qt.Qt.Key_Plus, qt.Qt.Key_Equal):
            self.setFont(delta = +2)
        elif ev.key() == qt.Qt.Key_Minus:
            self.setFont(delta = -2)
        elif ev.text():
            self.searchtext += str(ev.text())
            self.outlines.append(subgraph.LazyList(
                subgraph.subgraph(self.outlines[-1], self.searchtext)))
        self.drawSearchString(qt.QPainter(self))  # for rapid response
        self.redrawTimer.start(0)
    def drawSearchString(self, pa):
        pa.save()
        pa.setPen(self.darkGray)
        pa.setFont(self.font)
        pa.drawText(10, 20, self.searchtext)
        pa.restore()
    def drawHelp(self, pa):
        pa.save()
        pa.setPen(self.gray)
        pa.setFont(self.font)
        pa.drawText(110, 20, "Type a search string; + and - zoom")
        pa.restore()
    def redraw(self):
        self.redrawTimer.stop()
        pix = qt.QPixmap(self.size())
        pix.fill(self, 0, 0)

        pa = qt.QPainter(pix)
        pa.setFont(self.font)
        self.drawSearchString(pa)
        self.drawHelp(pa)
        self.drawKids(pa, 10, 45, self.outlines[-1])
        pa.end()
        qt.QPainter(self).drawPixmap(0, 0, pix)
    def paintEvent(self, ev):
        self.redraw()
    def drawKids(self, pa, x, y, outline):
        for text, kids, othercrap in outline:
            (x, y) = self.drawString(pa, x, y, text)
            (crap, y) = self.drawKids(pa, x + 20, y, kids)
            #y += 5
            if y > self.height(): return (x, y)
        return (x, y)
    def drawString(self, pa, x, y, string):
        linespacing = pa.fontMetrics().lineSpacing()
        for line in string.split('\n'):
            pa.drawText(x, y, line)
            y += linespacing
        return (x, y)
def main(argv):
    a = qt.QApplication(argv)
    if len(argv) < 2: target = '.'
    else: target = argv[1]
    w = outlineview(None, target)
    a.setMainWidget(w)
    w.show()
    w.setFocus()
    a.exec_loop()
if __name__ == '__main__': main(sys.argv)

Here's a tarfile:
begin 664 outlineview-0.tar.gz
M'XL(`#8FLS\``^U<X7/;MI+/9_T5J/*!U)16)"=.;M3G>Y/WFKZ7>6Z;7-+I
MW3@>/TB$+#Y3!$V0MI2YN;_]?KL`*)*2G$R;I+VK,--&)A:+W<7N8K%84E=E
MFF3J-E%W1Z-'#SY+&XV>C)X].\&_H]&SIT]:[EMAIL PROTECTED]">/#U^/*+GX\?CX]$#
M<?)YR&FWRI2R$.+!=2&O5+8?[D/]_T>;;JU_XZ]AOOY4<XS&6.^3?>L_'HU/
MGOKU/WY&O\=/1R=/'[EMAIL PROTECTED]'7_^'7SVJ3/%HFF2/\G6YT%DO6>:Z*,5-
M&0FS-OA?-;TJ9+Z(Q-SPC]Y#D62E*N2L3&X5?L\*M519*5-Q5:A<'!4TQI1)
M695JV`/XVQ^__7&"?X_$5)48*>9*Q5,YNQ8Z$W<+E0FC9#%;B,2(F5[FJ2H5
[EMAIL PROTECTED],H$R`MEJK0T&)"DRHU(LJMO!-8%U*3I&N@)*M>9(<H8$'C$!MAB
MM9,M=:&$FL^360+J,7JN"Y'J[$J8L@"L$4='1R(I`R/T]#;1E0&,T4MUMY`E
MHY7QK03?5PI]HM3"7">[EMAIL PROTECTED],*5:&K'650!:9%HH&:]%K&9)K&(1ZRPH
MP2R&)QDCHR&I+*Z4*45>Z!Q8\D+-DY70<^ZT5$5"[EMAIL PROTECTED]""(QK4I(<*;H[RM5
[EMAIL PROTECTED](:FRFK^1Q]S?E3K:\QO<3J,F>`R9.8B+_2&%3HZFHA?A"INE6I84R8
M?9ZD6#5,/F3Y6=IIT9=34`G0`JJ#;I(@_06=$&?R_?HL,:49"O$6>[EMAIL PROTECTED]@A
M[^3Z*\:3R=OD2I8)E(`$4>[EMAIL PROTECTED]&)%A7R(Z7N9]D_8B)
MF^LTU7=@:,JL&941&1C(_-MUMG+C&4D&PCDY(^:[EMAIL PROTECTED]<07E`HMX$D*V.L^!
MAT5"?M`.M0)EH+7!NM(BR,RC&EBQJ)DN9*DFXBXI%^*FTJ6"_<PT=`K_DKZ;
M5)J%,@R.-;[EMAIL PROTECTED]@[EMAIL PROTECTED]<R-`K=_J<H2P_XLQ+=:&9+WZU(L)%:2-%!<
M)[EMAIL PROTECTED]:$$?V;D,LT74DQ3E<4DD#M=I3%9!PQ4ED#_^N42:LN3OGZ5
MK)8RM\CMN*4TUV;(R%__%>07Z&1^N1LF/0,?1C1VB_"F'+[^F0D83'H8!VG,
MQ>5EDB7EY65H5#J/R)!A:)$H2<L]'+7-X.&>(3_H3$4U_*Y&.,KASV]**-3L
MKU`GC#/BO_WS_U`Y#*W\0;\HI,&"^6$TS=`2)$X=9>W..7"]2=XK=/];N\?J
M5ZE6-#0(NITE3\7R8VZ&\$=E=VJ`?:=GE7FETV2V;HAQ^`8&D5UQ9V<,7)PJ
M?[3"#W?AR\KNXT+%A;Q[FRQAG*<L$_[MI!S8;E'2LZ`S%*:9J5D9=O%$A.;-
MR[_]\/PL#&@DU"$<!(,]R]08/[EMAIL PROTECTED],[EMAIL PROTECTED]:D%V>YV/#&N
MX*?B3KR"GY!QHD4:1&TL'>ZJ/(;)AAN26L(ED"XYM?LX%>=N1QS>R?1Z;L*&
M,@TN:HS7:OT*6,V+6U6SJFX;:"6<`[#!6,/6#(-H\VRC:P/QM1AOQAJCL%M+
M<7HJIA$<&'!M6$SFF&D(`L(!`5A;^(=:7_Z%G%$N9VK26JUDWM7LR=9J;JM^
MY\GYY&A\L7N8YVR8Z[RAHBIM$HHM([EMAIL PROTECTED],4-W/[EMAIL PROTECTED]
MQ_LF:$GB^R2KS$>@[EMAIL PROTECTED]'V]"U5&2>PV&'/N(:^CTT
MW):F!_$_VIISCA6(NJ0,!AW=)UM\P]UO>(MDJ;^2'-Q9U1\(\9##(DR!*,'%
M5FJO;QE2,%V&HXTY;<WA_7I#;+D<&FP^#86@)[EMAIL PROTECTED]
MN]7+))'`QZ-('(^VA=$$!E\E(I>P3?;?59K_(G*O?BFIGM;^VW6.O;@=P'P#
MNZ?]^DB\UWK9_P`#=E%V.K#V>K4,,4>T:[EMAIL PROTECTED]@C&
[EMAIL PROTECTED](\2B<)@'\8Z6Q6RTA_6T(7IV=/?](8H.>2)`XGYQ$8LLT6N20Q6T>;)D`
MHW32&#&S-3\D9XXJ]GKVAKP[RL5$>N6*Q"[EMAIL PROTECTED])S<(R/9(72-$?#'\H$88
[EMAIL PROTECTED]>0E'7C;_X2$:N"],LO22]%/T]9_'D0HN^-J.:Z@>:26:TM%>^C#-;FX
MD^XVLA;_;C$M5'*U("\)E2RK(G,$UO#;3VNWT788GGIK#0T9\;)B+Z-`]Y16
ME'3J>P6PF0D'0^I^8[L;Z\QG/8J:(4>+<6CR-$'X\"X+.BZ]::.6!AOT-V%8
M"@U*]O!'O"VA,B&"!*\HWEZ>[EMAIL PROTECTED]<AV]USLJ0@@!^(/XGCB:BCU6!H8TZ<
MU%3C,8&>NQWX#G\WPW2.HWT`;F<G>_P>)-FP,W31V=V0SMYA_8<+4T,_2*W4
M[!(G+O(>"47ZF5RJRTO:6X/+2V+Q\C*86%YQ9!I:AG[K;,>A=5L[_\>A1*;+
M3YC\>_#!_-_CT6.7_SUY-AH]&0/^!,\.^;\OT;;S?P\%Y7-$E<UQ$C<+%0^%
M8+TXHNP).=F9CA5E!]Y2DBM6DM)VY4*6(D.'@?_AW%<AIZGBE`9.C_!IAC))
M"C[&?".41%A#OX&%4PN\)U$>)[EMAIL PROTECTED]&:':!<42U5*
M'-XD<#"<6N4IW(T1E#3$H#M)Z2V$13$Y>9^_$:$:[EMAIL PROTECTED];,)RHB8<(88RDW",S
M2Z<[=G5AO]]_";BLY("L2&2:O+=)*^PA2\Q+6XG=0HZ(I2-F`9,R'H--X5KU
M(%FC;,+II^SU6<1RHU26]%B6VE`.TE1)R8(CK(MJ*;.>$_M0O`R6XDK3"E"N
MD2)MFVG:3U>O15!9*$I(E91PE7',&:E"S56ALIGB!.927M/:^8SJ$+Q'XOR"
M_L.Y/NP_=XS:>3-=)[=\LBM3=[P)(E+!_NKTA6*80B8I#Z0<")\\AQW4F5U5
M4]41+Z7!\!`:0N<[EMAIL PROTECTED],Z-AT<;Q>)\10RTYRT(AG84;-%DL;@MA_U
MQ'ECRC9/GNP]I))*/.>$'8]N]#4)44XT;`V4P:ZSN.NLE"NP]D9SG*0KJUQ>
[EMAIL PROTECTED]:4;&(<N:^JDGJ+.CT-K7[7KT5?&2+ZW3N6DYE)G"!L%^`,U&V+
M"2+2YD]I8)O.6HX3B&/)?)#=%3*#_>YD^;[EMAIL PROTECTED];AU4B2$6,Q2M`
M"6W2#1KL+.L[V`/]&?;!US92"_166Y"[EMAIL PROTECTED]("]$02-@(32M%:"GV].
MTH6U8WZI;]E^$(!J)@(*^WGY!8M+766\;!N%I7L5NH-1-Q5.)C8N15P-I[SD
MGTD6\W/[EMAIL PROTECTED]"Z>`]:,;@[EMAIL PROTECTED])\MK%^F2XU"61_$>7\PUK'%UU;W2"IP
M[62XTAN:G!7:&+&LTC*!O[7D-<34MZJ9%,:23KQ)YZ8]#L*;9+.T`C4[#+;W
MILO]$AL]9<#A)J\RX%$)VSX;?=F:+NKIQG--KH?GALV>[EMAIL PROTECTED];B=T2
MR^UD2G</FC>D'N]:;%AP%9[:>C/RUSETB3.O"J;&XK!>G!XHT9O!^.Q5FMG!
MJ&!`VEML!HDO#G9YVQZL([EMAIL PROTECTED])BNC[!T04:>D2;#]W"8FP>X#/"]A
MND5,5UD:,H.3@"YCXZ>C35)&M<;[EMAIL PROTECTED]'53J(.D![V9+9N<C-E
MQ6M!@>Z<<K+%+?;H]GY46P,K'.V.Q([EMAIL PROTECTED]<D!>#J;W%J5W8V'+,/,BF1*
MVJ7%7!91&X+5((-T%CIEX[&\S1:J::,$.:.[&=[EL=JZ*!#YM-:4Y;"Y!R)4
MUD>W:0J"UE\O^>KL6EB%GK$]^*V,Y&-O;"7=DTC&^9_?GPV#>Q"^XKO;&<(M
MN3ESPD3HI.[U-*CM,=A<=/&-F"J"SB(`Q.>JJA*:5Z[9`&H4`,B+BG2#0Q!K
M+=9V6:[L*2DR;%M4P6&B0TQNR`3^<J$U/=#'"4[O<LV>?]G(C3#!77(!;Y'R
M"M2WU^EZ"[#+)&]KN88(YE6ZBV5K*GSKV^02'K"0EL6&4/H4XZD5!YW6HNP*
M<#Q`(9XB=Y5:\>`DG2+DA7"NG(UQU%PL^[5,?,XA[$]MJ$!WC%-$D^L62D)'
M.]$D$F_UQ!HGW.6_H*P3P.C^Y@:G+0V2P89*F<)8/%ZULHSRY62'3^?3OVLR
M:R,?VD/<'2N8`;$(F>W5JG`E!_5TO>Y,[EMAIL PROTECTED]".9*4HH`*\>12B=RL1
M'[EMAIL PROTECTED]>G'-9+HYPT*#[G,2'16VM@(U"?5-$2A4M57>^L`_2%)=8
MJ.R*"*1`SZJO0.A?I3B.TK%FQT"*91.<:$2(8`I1,8>W+Y9R!MO%MCS@@@52
MJOJ.BCP%0D:C=I%!LQQ!]4&NO=7>[US;;NYG%4`M,T6%!+',09%I:+]7?GO9
MSOL:'[SLL:2YT9-.3_4JNH_,)1:5STE46Y+;HQ&P?<.!)9MX7H?P$87OBP1'
M%]CE>B?+"?ED3?X%GLC5-NQ<)`I84CJ/OL>^",2RK+C&)*(]PN]^I+RV:&,?
MGC>OSUC.4VDH2'?K!J-+L6U3K45MHK1WV=([EMAIL PROTECTED])\3,.`MX<[EMAIL PROTECTED]
M?!;?MVP7/<[EMAIL PROTECTED]"[EMAIL PROTECTED]"MH$4;EX%\:.M5YK290$;5E(I=ZM*8&38^
M[%D^&^G&#TN5IN%`?'5*B";U8WB8ZY!0,WP&SS>CP[3O)CK"L<LBVKM+#W,J
[EMAIL PROTECTED]"9YMI`%7[>ZG"8+:=Q(`-=`_@:M,UOK0G0#35<!;MIVPK>!
ML'U12<TG=([EMAIL PROTECTED]"\$>.$&>!!UA!.)H\?CQX^?#7S%1+T4JJ!,^MY*B>8:12Z"
MZ%XKU##\%P/R+Q>'GNY$TIP0FX>;<).<I[]J&%J([EMAIL PROTECTED]@[EMAIL PROTECTED])=M-
MX7;6WZ+;NMYLR*F#JCWQCF(#:/X.:7^4I$]''N]I$'Q6<7?ET-&+/>)KS3BP
MQHW8L;Z"]ME;]K:G[)'"P*;N\)2#/%?<X2$+K4L^T)VV1-Y$9`<050`B)L+N
M8`O!YR>G'?P;0]Q]59+--083CB'K5,O\-P/M[;&AV":\/^?6;V%(Z8K<S\G7
M^\Y3T(S']Y/M4#"D(^Y\=/%+*6FP""Q$2;^_O[\URY[46G\C_^,=PKN/[OT8
[EMAIL PROTECTED]/\9C8]'QZ[^>_P4VR7=_XS'A_KO+]*V
M[W\X?7%Y.:^P,Y%_</[EMAIL PROTECTED](4X;TDV\*1#O/:QK6F6]PQR9<ITJ6S`^
MIQ0/G\U^RI)5HYH6(\.2*E,YEYCPM9$KX)2&W3N?9&Q1'&][EZ[BKM0Y0GJW
M_]')AT:38^,<@!G26`"TX3C"<&7F=*"C.W)$>OAE(SH+'-6X-F'+.E%I7#^/
MA*.BB6P0M7#;77H7M?#!?M;$-&FL8[&M,K`.UX/&[?_>,42LE^9F(%/5ZF+:
M'64))#*J1>K+)#B>:(!M!'(3#V<+,!WB1Y7AL)/,UR'72`SLT82'(2A*&I%X
M0A43X]YAI_E-6]O_8_T^K>OG1O[_:<?O-]__>O+LB??_QR<C>O_K"?:[EMAIL PROTECTED]
M$HT3!&R[7!4V:9RPN9;W:#RY(.,_#]Y1VI>JHRXFPA7^MJM]G0NBA[V>BT\M
MZD!2415'FC+H=-4]W8[Z,?NJVJTTR.1"L6HYO437I5KE(9W"&\XI(=:PN%\!
MS;LRJ)TD/>R>P`+Z'33Y\.=[*OQKSN+JYP;_/SQ7V_[K[>-+UO\<GSP;;^I_
MGIYP_<_X4/_S1=JOB/\0N_6HM(]#&)=X"Q[!>-4C*ZQ',=TO!P,_P,>$]$J@
MCT\FB`93N5;%YKTY!"E\XYO9*B+*=W-RV;]P9O"<+P7YG3RJ5Y)IQ7>?+_U%
M&*5IVS<Q]3TVO6]H[UKIZK,NI[%&#V0R<^^RT:G97B([EMAIL PROTECTED]"MS[
MB$.7;:HYVI=J,NH&P9VK0[_QR1Q^2="[EMAIL PROTECTED](Z'DSZKAX\IB
MW<Y=6M]=OS?"^`?B3Z=VY*0Q:_VZ@:/(I1$:KSFL9BHOQ1LLR$M>B41G.U]V
MN/$,G%]T<J4R,4J\I)E?%(4NPCX%Y)[EMAIL PROTECTED])-27\K-;BA\YS!FT+)=/9>
M%7H[G4;2X)'GHXLN&[EMAIL PROTECTED](%I!M$(2GG/J=^M<)=1]K[/E,WSALMX\75L
MY.[YL%WRB&89=Y7%+H=68W5C-EBWLM3#><+KXP%H)\.&[*)NGVSSN-WT;9[:
MM=H-*^:ZA+*H%+V_F?&;0LN\7-MZA#ME;W?<C6$R[Z#9\).8UKB=-D/&XL\9
MPPXFLAALZP1+=3M:"P7745!BEJY'NN!O`3"5L:U0BMV;D7>ZN&X#-N6].0GY
MAVT=!>=USX=$:\^>J:79JL7'ZD(;LT?!L`./GK!WD'N=Y!QG9V9WC-/7]JVO
MR9Z7P2Q,:JZV]#AR/[EMAIL PROTECTED]/[EMAIL PROTECTED]/MA+1_N_+%+)GRALV>G;67)3S
M<22.(_'XHJ7]9V<[EMAIL PROTECTED]<=*N6,N\7O3`])7D0,?-)_R[&=GY%:W9_I0
M?X?43T.C?6I1=834785S1,Z;FH0+(K+SQ.:"[?N#'CP<N)JQEIKO'ND5!HYE
MVIU=#NIW":[EMAIL PROTECTED]:[EMAIL PROTECTED](3#@;W`/O'.X%W=G<0K`"R:G#C
M1_=:_':ANDA`\\K.N`-?E[`.ZH\:NV]"=T$K/@,C'S/%/1/=Q]:NF3W0KY^Y
M@>DSSLQJNG_F-J9Z9G=)[EMAIL PROTECTED]:V.^53HS.PA<<:[EMAIL PROTECTED]/@
MM\2X'X$2AWCMU[LHQN&WD-2-WR3HT28>XAC*SE`CM(9[R7TF+!=5=DV;R*P;
M"W%/N!72X9\&A=QA`R">ZSDC!QLN9OLVB?F##]39CT1CMHYDO*NL=V8N`<$\
M)<<[EMAIL PROTECTED]>Z+_:QVX$=3Z^H+UF7,^19(9"^\1FB64=1]63Q9JC\\2'
MJCQNU8RX&MA)3U@/!PX]A2]I(QBQ3#2\I!=V*I?36$[LCKP"&KLT'H^KZ:;(
MK5$"Z8L&N1+:Q0`$9C^EX6,M^CB'0R-M]2+',/7QI$;[EMAIL PROTECTED]
[EMAIL PROTECTED](MO1TB>=2U'9I.I%*1+JO4<3W2:I)I<D_CLI=7Q%H"]J6R+H"HX7,
[EMAIL PROTECTED]>KJ].I2.JX<L\-]^0N.G%P,3>`U]U9?M2W1<@?$^W2JN7.UZLMV
MM>:6Z39W?E('`:O6>FVZ6NMUK53.:\'53UPMAX"[J4_U1UV(??L.PO*>M?*S
M=M7:),LDE04=K'?K;6N>[EMAIL PROTECTED]/);BZG^2<"UQ*V=[6I"V7-$L^#LE#66O%V9;
M\+M4?H=0&X[?O5!)_O:T9M2+FQ[?:[EMAIL PROTECTED]@Y=WS7%MJ%32"[EMAIL PROTECTED])>)W]
MH/SKT([EMAIL PROTECTED]@\.P3,R,S!+[#T>/_-C^[6[N[=&U\<1N"6J5JQD5T&]>;/K4
M;[EMAIL PROTECTED]"L)G4-3H+6QP
MK]_O_;X*;.[[_M__?*(Y/E#_\?3D<?W]Q_%H?$S?_SM^?,C_?Y'VX>__>8W?
M_27`S:?]#M]6^^V_K79*WW?X'7U8[?#MM,.WTYHC[OEV6C`,#M],VS7L\,VT
MPS?3#M],.WPS[?#-M,,WTUP[?#/M_F^F=;]Z=OC2V:$=VJ$=VJ$=VJ$=VJ$=
>VJ$=VJ$=VJ$=VJ$=VJ$=VJ']P=K_`ESA(UP`>```
`
end

Reply via email to