From 93fc4699a94e2d643c34ee300e8844221cb304fa Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Sun, 24 Jun 2007 15:04:19 +0000 Subject: [PATCH] Type coercion for luajava package, make luajava an "addon" --- .classpath | 1 + .../lua/addon/luajava/CoerceJavaToLua.java | 65 +++++++ .../lua/addon/luajava/CoerceLuaToJava.java | 144 ++++++++++++++ src/addon/java/lua/addon/luajava/LuaJava.java | 182 ++++++++++++++++++ src/main/java/lua/GlobalState.java | 17 +- src/main/java/lua/LuaJava.java | 125 ------------ src/main/java/lua/value/LDouble.java | 6 +- src/main/java/lua/value/LInteger.java | 2 +- src/main/java/lua/value/LString.java | 10 +- src/main/java/lua/value/LValue.java | 7 +- src/test/java/LuaJavaAppRunner.java | 49 +++++ src/test/java/LuacRunner.java | 5 +- src/test/res/compile.sh | 4 +- src/test/res/swingapp.lua | 20 ++ src/test/res/swingapp.luac | Bin 0 -> 1319 bytes src/test/res/test1.lua | 79 ++++++++ src/test/res/test1.luac | Bin 138 -> 5884 bytes src/test/res/test7.luac | Bin 0 -> 489 bytes 18 files changed, 574 insertions(+), 142 deletions(-) create mode 100644 src/addon/java/lua/addon/luajava/CoerceJavaToLua.java create mode 100644 src/addon/java/lua/addon/luajava/CoerceLuaToJava.java create mode 100644 src/addon/java/lua/addon/luajava/LuaJava.java delete mode 100644 src/main/java/lua/LuaJava.java create mode 100644 src/test/java/LuaJavaAppRunner.java create mode 100644 src/test/res/swingapp.lua create mode 100644 src/test/res/swingapp.luac create mode 100644 src/test/res/test7.luac diff --git a/.classpath b/.classpath index b37a9cfe..c09db0ba 100644 --- a/.classpath +++ b/.classpath @@ -1,6 +1,7 @@ + diff --git a/src/addon/java/lua/addon/luajava/CoerceJavaToLua.java b/src/addon/java/lua/addon/luajava/CoerceJavaToLua.java new file mode 100644 index 00000000..c54b9eb5 --- /dev/null +++ b/src/addon/java/lua/addon/luajava/CoerceJavaToLua.java @@ -0,0 +1,65 @@ +package lua.addon.luajava; + +import java.util.HashMap; +import java.util.Map; + +import lua.addon.luajava.LuaJava.LInstance; +import lua.value.LBoolean; +import lua.value.LDouble; +import lua.value.LInteger; +import lua.value.LNil; +import lua.value.LString; +import lua.value.LValue; + +public class CoerceJavaToLua { + + public static interface Coercion { + public LValue coerce( Object javaValue ); + }; + + private static Map COERCIONS = new HashMap(); + + static { + Coercion boolCoercion = new Coercion() { + public LValue coerce( Object javaValue ) { + Boolean b = (Boolean) javaValue; + return b.booleanValue()? LBoolean.TRUE: LBoolean.FALSE; + } + } ; + Coercion intCoercion = new Coercion() { + public LValue coerce( Object javaValue ) { + Number n = (Number) javaValue; + return new LInteger( n.intValue() ); + } + } ; + Coercion doubleCoercion = new Coercion() { + public LValue coerce( Object javaValue ) { + Number n = (Number) javaValue; + return new LDouble( n.doubleValue() ); + } + } ; + Coercion stringCoercion = new Coercion() { + public LValue coerce( Object javaValue ) { + return new LString( javaValue.toString() ); + } + } ; + COERCIONS.put( Boolean.class, boolCoercion ); + COERCIONS.put( Byte.class, intCoercion ); + COERCIONS.put( Short.class, intCoercion ); + COERCIONS.put( Integer.class, intCoercion ); + COERCIONS.put( Float.class, doubleCoercion ); + COERCIONS.put( Double.class, doubleCoercion ); + COERCIONS.put( String.class, stringCoercion ); + } + + public static LValue coerce(Object o) { + if ( o == null ) + return LNil.NIL; + Class clazz = o.getClass(); + Coercion c = COERCIONS.get( clazz ); + if ( c != null ) + return c.coerce( o ); + return new LInstance( o, o.getClass() ); + } + +} diff --git a/src/addon/java/lua/addon/luajava/CoerceLuaToJava.java b/src/addon/java/lua/addon/luajava/CoerceLuaToJava.java new file mode 100644 index 00000000..17a00e34 --- /dev/null +++ b/src/addon/java/lua/addon/luajava/CoerceLuaToJava.java @@ -0,0 +1,144 @@ +package lua.addon.luajava; + +import java.util.HashMap; +import java.util.Map; + +import lua.StackState; +import lua.addon.luajava.LuaJava.LInstance; +import lua.value.LBoolean; +import lua.value.LDouble; +import lua.value.LInteger; +import lua.value.LNil; +import lua.value.LNumber; +import lua.value.LString; +import lua.value.LValue; + +public class CoerceLuaToJava { + + public static interface Coercion { + public Object coerce( LValue value ); + public int score( LValue value ); + }; + + private static Map COERCIONS = new HashMap(); + private static Coercion OBJECT_COERCION; + + static { + Coercion boolCoercion = new Coercion() { + public Object coerce(LValue value) { + return value.luaAsBoolean()? Boolean.TRUE: Boolean.FALSE; + } + public int score(LValue value) { + if ( value instanceof LBoolean || value == LNil.NIL ) + return 0; + if ( value instanceof LNumber ) + return 1; + return 4; + } + }; + Coercion intCoercion = new Coercion() { + public Object coerce(LValue value) { + return Integer.valueOf( value.luaAsInt() ); + } + public int score(LValue value) { + if ( value instanceof LInteger ) + return 0; + if ( value instanceof LNumber ) + return 1; + if ( value instanceof LBoolean || value == LNil.NIL ) + return 2; + return 4; + } + }; + Coercion stringCoercion = new Coercion() { + public Object coerce(LValue value) { + return value.luaAsString(); + } + public int score(LValue value) { + if ( value instanceof LInstance ) + return 0; + return 1; + } + }; + Coercion objectCoercion = new Coercion() { + public Object coerce(LValue value) { + if ( value instanceof LInstance ) + return ((LInstance)value).instance; + if ( value instanceof LString ) + return value.luaAsString(); + if ( value instanceof LInteger ) + return Integer.valueOf(value.luaAsInt()); + if ( value instanceof LDouble ) + return Double.valueOf(value.luaAsDouble()); + if ( value instanceof LBoolean ) + return Boolean.valueOf(value.luaAsBoolean()); + if ( value == LNil.NIL ) + return null; + return value; + } + public int score(LValue value) { + if ( value instanceof LString ) + return 0; + return 0x10; + } + }; + COERCIONS.put( Boolean.TYPE, boolCoercion ); + COERCIONS.put( Boolean.class, boolCoercion ); + COERCIONS.put( Byte.TYPE, intCoercion ); + COERCIONS.put( Byte.class, intCoercion ); + COERCIONS.put( Short.TYPE, intCoercion ); + COERCIONS.put( Short.class, intCoercion ); + COERCIONS.put( Integer.TYPE, intCoercion ); + COERCIONS.put( Integer.class, intCoercion ); + COERCIONS.put( Long.TYPE, intCoercion ); + COERCIONS.put( Long.class, intCoercion ); + COERCIONS.put( String.class, stringCoercion ); + COERCIONS.put( Object.class, objectCoercion ); + } + + static Object coerceArg(LValue v, Class type) { + Coercion co = COERCIONS.get( type ); + if ( co != null ) + return co.coerce( v ); + if ( v instanceof LInstance ) + return ((LInstance) v).instance; + return v; + } + + static Object[] coerceArgs(StackState state, int base, int nargs, Class[] parameterTypes) { + int n = parameterTypes.length; + Object[] args = new Object[n]; + for ( int i=0; i nargs? 0x4000: 0x8000); + for ( int i=0; i= 0) + state.adjustTop(base + nresults); + } + + static class LInstance extends LValue { + Object instance; + private Class clazz; + public LInstance(Object o, Class clazz) { + this.instance = o; + this.clazz = clazz; + } + public String luaAsString() { + return instance.toString(); + } + public void luaGetTable(StackState state, int base, LValue table, LValue key) { + final String s = key.luaAsString(); + try { + Field f = clazz.getField(s); + Object o = f.get(instance); + LValue v = CoerceJavaToLua.coerce( o ); + state.stack[base] = v; + state.top = base + 1; + } catch (NoSuchFieldException nsfe) { + state.stack[base] = new LMethod(instance,clazz,s); + state.top = base + 1; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + public void luaSetTable(StackState state, int base, LValue table, LValue key, LValue val) { + Class c = instance.getClass(); + String s = key.luaAsString(); + try { + Field f = c.getField(s); + Object v = CoerceLuaToJava.coerceArg(val, f.getType()); + f.set(instance,v); + state.top = base; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + } + + private static final class LMethod extends LFunction { + private final Object instance; + private final Class clazz; + private final String s; + private LMethod(Object instance, Class clazz, String s) { + this.instance = instance; + this.clazz = clazz; + this.s = s; + } + public String toString() { + return clazz.getName()+"."+s+"()"; + } + public void luaStackCall(StackState state, int base, int top, int nresults) { + try { + Method[] meths = clazz.getMethods(); + Method meth = null; + Class[] paramTypes = null; + int score = Integer.MAX_VALUE; + int paramsBase = base + 2; + int nargs = top - paramsBase; + for ( int i=0; i= 0) - state.adjustTop(base + nresults); - } - - static class LInstance extends LValue { - private Object instance; - public LInstance(Object o) { - this.instance = o; - } - public String luaAsString() { - return instance.toString(); - } - public void luaGetTable(StackState state, int base, LValue table, LValue key) { - Class c = instance.getClass(); - final String s = key.luaAsString(); - try { - Field f = c.getField(s); - Object o = f.get(instance); - String v = String.valueOf(o); - state.stack[base] = new LString( v ); - state.top = base + 1; - } catch (NoSuchFieldException nsfe) { - state.stack[base] = new LMethod(instance,s); - state.top = base + 1; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - public void luaSetTable(StackState state, int base, LValue table, LValue key, LValue val) { - Class c = instance.getClass(); - String s = key.luaAsString(); - try { - Field f = c.getField(s); - String v = val.luaAsString(); - f.set(instance,v); - state.top = base; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - } - - private static final class LMethod extends LFunction { - private final Object instance; - private final String s; - private LMethod(Object instance, String s) { - this.instance = instance; - this.s = s; - } - - public void luaStackCall(StackState state, int base, int top, int nresults) { - Class c = instance.getClass(); - try { - Method m = c.getMethod(s,new Class[0]); - Object o = m.invoke(instance,new Object[0]); - String v = String.valueOf(o); - state.stack[base] = new LString( v ); - state.top = base + 1; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } -} \ No newline at end of file diff --git a/src/main/java/lua/value/LDouble.java b/src/main/java/lua/value/LDouble.java index 779cb178..33c77ce9 100644 --- a/src/main/java/lua/value/LDouble.java +++ b/src/main/java/lua/value/LDouble.java @@ -40,7 +40,7 @@ public class LDouble extends LNumber { case Lua.OP_MUL: return new LDouble( lhs * rhs ); case Lua.OP_DIV: return new LDouble( lhs / rhs ); case Lua.OP_MOD: return new LDouble( lhs % rhs ); - case Lua.OP_POW: return new LDouble( Math.pow(lhs, rhs) ); +// case Lua.OP_POW: return new LDouble( Math.pow(lhs, rhs) ); } return luaUnsupportedOperation(); } @@ -49,6 +49,10 @@ public class LDouble extends LNumber { return (int) m_value; } + public double luaAsDouble() { + return m_value; + } + // binary compares on integers, first dispatch public boolean luaBinCmpUnknown(int opcode, LValue lhs) { return lhs.luaBinCmpDouble( opcode, this.m_value ); diff --git a/src/main/java/lua/value/LInteger.java b/src/main/java/lua/value/LInteger.java index 49ae83df..51b799ff 100644 --- a/src/main/java/lua/value/LInteger.java +++ b/src/main/java/lua/value/LInteger.java @@ -35,7 +35,7 @@ public class LInteger extends LNumber { case Lua.OP_MUL: return new LInteger( m_value * rhs ); case Lua.OP_DIV: return new LInteger( m_value / rhs ); case Lua.OP_MOD: return new LInteger( m_value % rhs ); - case Lua.OP_POW: return new LInteger( (int) Math.pow(m_value, rhs) ); +// case Lua.OP_POW: return new LInteger( (int) Math.pow(m_value, rhs) ); } return luaUnsupportedOperation(); } diff --git a/src/main/java/lua/value/LString.java b/src/main/java/lua/value/LString.java index bf4082cd..6a76eb1b 100644 --- a/src/main/java/lua/value/LString.java +++ b/src/main/java/lua/value/LString.java @@ -6,17 +6,23 @@ import lua.StackState; public class LString extends LValue { final String m_string; + final int m_hash; public LString(String string) { this.m_string = string; + this.m_hash = string.hashCode(); } public boolean equals(Object o) { - return o != null && o instanceof LString && m_string.equals(((LString)o).m_string); + if ( o != null && o instanceof LString ) { + LString s = (LString) o; + return m_hash == s.m_hash && m_string.equals(s.m_string); + } + return false; } public int hashCode() { - return m_string.hashCode(); + return m_hash; } // TODO: what to do with LuaState? diff --git a/src/main/java/lua/value/LValue.java b/src/main/java/lua/value/LValue.java index 1f89bc52..85348c84 100644 --- a/src/main/java/lua/value/LValue.java +++ b/src/main/java/lua/value/LValue.java @@ -7,7 +7,7 @@ abstract public class LValue { protected static LValue luaUnsupportedOperation() { - throw new java.lang.UnsupportedOperationException(); + throw new java.lang.RuntimeException( "not supported" ); } public String id() { @@ -106,6 +106,11 @@ public class LValue { return 0; } + /** Return value as a double */ + public double luaAsDouble() { + return luaAsInt(); + } + /** Arithmetic negative */ public LValue luaUnaryMinus() { return luaUnsupportedOperation(); diff --git a/src/test/java/LuaJavaAppRunner.java b/src/test/java/LuaJavaAppRunner.java new file mode 100644 index 00000000..aff73468 --- /dev/null +++ b/src/test/java/LuaJavaAppRunner.java @@ -0,0 +1,49 @@ + +import java.io.IOException; +import java.io.InputStream; + +import lua.GlobalState; +import lua.StackState; +import lua.addon.luajava.LuaJava; +import lua.io.Closure; +import lua.io.LoadState; +import lua.io.Proto; +import lua.value.LString; + +/** + * Program to run a compiled lua chunk for test purposes, + * but with the LuaJava add-ons added in + * + * @author jim_roseborough + */ +public class LuaJavaAppRunner { + + public static void main( String[] args ) throws IOException { + + // add LuaJava bindings + LuaJava.install(); + + // get script name + String script = (args.length>0? args[0]: "/swingapp.luac"); + System.out.println("loading '"+script+"'"); + + // new lua state + StackState state = new StackState(); + + // push args onto stack + for ( int i=1; i0? args[0]: "src/test/res/test1.luac"); + String script = (args.length>0? args[0]: "/test1.luac"); System.out.println("loading '"+script+"'"); // new lua state @@ -30,7 +29,7 @@ public class LuacRunner { state.push(new LString(args[i])); // load the file - InputStream is = new FileInputStream( script ); + InputStream is = LuacRunner.class.getResourceAsStream( script ); Proto p = LoadState.undump(state, is, script); // create closure to execute diff --git a/src/test/res/compile.sh b/src/test/res/compile.sh index f569c37a..3867edee 100644 --- a/src/test/res/compile.sh +++ b/src/test/res/compile.sh @@ -1,7 +1,7 @@ #!/bin/bash LUA_HOME=/cygdrive/c/programs/lua5.1 -TESTS="test1 test2 test3 test4 test5" -TESTS="test7" +TESTS="test1 test2 test3 test4 test5 swingapp" +TESTS="swingapp" for x in $TESTS do echo compiling $x diff --git a/src/test/res/swingapp.lua b/src/test/res/swingapp.lua new file mode 100644 index 00000000..b58ec403 --- /dev/null +++ b/src/test/res/swingapp.lua @@ -0,0 +1,20 @@ + +frame = luajava.newInstance( "javax.swing.JFrame", "Texts" ); +pane = luajava.newInstance( "javax.swing.JPanel" ); +borderFactory = luajava.bindClass( "javax.swing.BorderFactory" ) +border = borderFactory:createEmptyBorder( 30, 30, 10, 30 ) +pane:setBorder( border ) +label = luajava.newInstance( "javax.swing.JLabel", "This is a Label" ); + + +layout = luajava.newInstance( "java.awt.GridLayout", 2, 2 ); +pane:setLayout( layout ) +pane:add( label ) +pane:setBounds( 20, 30, 10, 30 ) + +borderLayout = luajava.bindClass( "java.awt.BorderLayout" ) +frame:getContentPane():add(pane, borderLayout.CENTER ) +jframe = luajava.bindClass( "javax.swing.JFrame" ) +frame:setDefaultCloseOperation(jframe.EXIT_ON_CLOSE) +frame:pack() +frame:setVisible(true) diff --git a/src/test/res/swingapp.luac b/src/test/res/swingapp.luac new file mode 100644 index 0000000000000000000000000000000000000000..15ae1d2af5752c74a459fc9a6541e45f2cdbde50 GIT binary patch literal 1319 zcmaJ=OK;Oa5FW=H8&TdMBp^Z+;+zv_v`KK%qNw2!0k>)sZ;OlTMD~U>Hy5e=u5bYU z7TbR@**r>))pXj7XA1ZAsd=9v)+LdbOKL5N z&dH%ho;!Y7pR@3@eb?F!2v|MUAF56u{uOSBZyh9g|8!~q` z^+?dU6t>>dE%AV!>c~~+95p`|Hirz51O7!0&WStXHuq(KIPWqCNp4Y(Ip?0`EIni< zaPAiOA`dwJhW-!zbsCatn;5)_?cKLAd5Q?sz1 zm~nAl%a!m_79}!mRt`nWu^KU-|C;Js{XOIuevPtSeAMnr$+cs(n9K}`N06CjQ_W4Y zmquaV8dgPVQ*@;l0s$%vZNCMXSrTSzwy&hP4F}uVI^=P=h9$o?-G%8`o6aOLIx+mX zB$!?&I2Z;WtbDQ>>l5fv@QprFv)FXvNu~!=ohlPe5<8LL%l`0iaB$e^4?YI#F^<2H z*s-CXqAVK48W$q|Q`WYCo3DcVu7daEI`B%B!JSSgdgm%-1y4g9)Q@J3vR U`>V!Z literal 0 HcmV?d00001 diff --git a/src/test/res/test1.lua b/src/test/res/test1.lua index c349c309..90c2e697 100644 --- a/src/test/res/test1.lua +++ b/src/test/res/test1.lua @@ -1,5 +1,84 @@ +for cycle = 1,100 do + a = 123 + 456 print( a ) +i = 777 +while i<780 do + print(i) + i = i+1 +end + +a,b = 0,1 +while true do -- infinite loop + print( b ) + a,b = b,a+b + if a>10 then break end -- exit the loop if the condition is true +end + +for count = 336,330,-2 do print(count) end -- numerical iteration + +function sum(a,b,c,d) -- "sum" method + local d = d or 0 + return a+b+c+d -- return sum +end +print( sum( 1, 2, 3, 4 ) ) +print( sum( 5, 6, 7 ) ) +print( sum( 9, 10, 11, 12, 13, 14 ) ) +print( sum( sum(1,2,3,4), sum(5,6,7), sum(9,10,11,12,13,14), 15 ) ) + + +function f0() print( "f0:" ) end +function f1(a) print( "f1:", a ) end +function f2(a,b) print( "f2:", a, b ) end +function f3(a,b,c) print( "f3:", a, b, c ) end +function f4(a,b,c,d) print( "f4:", a, b, c, d ) end + +f0() f0( "a1/1" ) f0( "a1/2", "a2/2" ) f0( "a1/3", "a2/3", "a3/3" ) f0( "a1/4", "a2/4", "a3/4", "a4/4" ) +f1() f1( "a1/1" ) f1( "a1/2", "a2/2" ) f1( "a1/3", "a2/3", "a3/3" ) f1( "a1/4", "a2/4", "a3/4", "a4/4" ) +f2() f2( "a1/1" ) f2( "a1/2", "a2/2" ) f2( "a1/3", "a2/3", "a3/3" ) f2( "a1/4", "a2/4", "a3/4", "a4/4" ) +f3() f3( "a1/1" ) f3( "a1/2", "a2/2" ) f3( "a1/3", "a2/3", "a3/3" ) f3( "a1/4", "a2/4", "a3/4", "a4/4" ) +f4() f4( "a1/1" ) f4( "a1/2", "a2/2" ) f4( "a1/3", "a2/3", "a3/3" ) f4( "a1/4", "a2/4", "a3/4", "a4/4" ) + +function g0(a,b,c,d) return end +function g1(a,b,c,d) return d end +function g2(a,b,c,d) return c, d end +function g3(a,b,c,d) return b, c, d end +function g4(a,b,c,d) return a, b, c, d end + +z = g0("c0.1/4", "c0.2/4", "c0.3/4", "c0.4/4") +print( "z0:", z ) +z = g2("c2.1/4", "c2.2/4", "c2.3/4", "c2.4/4") +print( "z2:", z ) +z = g4("c4.1/4", "c4.2/4", "c4.3/4", "c4.4/4") +print( "z4:", z ) + +a,b,c,d = g0( "c0.1/4", "c0.2/4", "c0.3/4", "c0.4/4" ) +print( "g0:", a, b, c, d, "(eol)" ) +a,b,c,d = g2( "b2.1/4", "b2.2/4", "b2.3/4", "b2.4/4" ) +print( "g2:", a, b, c, d, "(eol)" ) +a,b,c,d = g4( "b4.1/4", "b4.2/4", "b4.3/4", "b4.4/4" ) +print( "g4:", a, b, c, d, "(eol)" ) + +function func(a,b,c) + return a, b, c +end + +print( func(11, 12, 13) ) +print( func(23, 22, 21) ) +print( func(func(32,33,34), func(45,46,47), func(58,59,50)) ) + +function p(a,...) + print("a",a) +-- print("...",...) +-- print("...,a",...,a) +-- print("a,...",a,...) +end +p() +p("q") +p("q","r") +p("q","r","s") + +end \ No newline at end of file diff --git a/src/test/res/test1.luac b/src/test/res/test1.luac index fcda0e645818f52fd9e23b8e562b1d6e291723cd..488cec33647daed516423ede4f53b9f09a280c3f 100644 GIT binary patch literal 5884 zcmcIo+io015UrlQIq@~N;}~KQCm2Y;tmC!0gP3+O31Ds#5<)_V^;MQ~Be5mm#WR*H z_y`_&<2QDKB7Oq#4SWC~>%&on3ZKkW9;Gs;aB{bWL}4&&uc5_M0DzDVNIwGAJVP z-q!uS#rnGau(WN9Skup;(BMbfA$*-Dihr6R`>=F2dTc~BCIc~Z>tLz+Yn(Qghh?*sIK|JPKa z${}J_z;zf&243?sxJy+@uT^E{Fe%wX$M5o(L!Py)Dsw5MsmWXhX(0{PK83W9SxqX4 znX!qJ)(rSHMW3|;lBTp1!x)>vHm4-Brx+8tzVwu&8T!m=F)ODT8&xousbAyHGcbWkdYv1SOhJcK$MLj4TM4C}7(yZtkR z$Y&V)IgEN8M!k-7#@dl~EVyktf_)vqzK&vFM>}KF(L!vG9Bdl>V#egVY*g~I$b9UW z9HAB;;(aO)MrAb`LmiJXzfy(yWw6Z|)Ylo*{TcN4;mOYQ=V9(<9BVR;H5td6jQ8g; z6McE+GOqQ6TgO$%G{srYaNcl+JI=ZDO~5w+-{e1hNAPEM67MsKb3BQ2Ezv{tndmIe z@mak8S%0oqCg6u1P2rB1l9@;%%!Awo`(MI-Phr2&&vOcCGwQe}gPxjm^mW*oWuHVB zlAjKqkVL+~eOv<2et$D4pT*?}b~8Ap594wb_T78CxA(-kc7yB2_pu-I<@bKfx{~Fr zb0I?ZgYx5^_DHK-2Jij-Tlpw;4X84v%A_jOs?4ZzNtLHnxva{ws?4iWkIPJX`>J@k z7-S>Jr68B3T;U=tE+8Ukh;dqDLAAvN#D_lBmh!&E1!ZaE+ET79Q)4bWA(x$y%TCB; zC*-n&IBv+dQm#R4T&+9epgZcIJMy4Adam!cS6>YS8-;<5dV64_-X7Sfw+A+K;BsN$ za$(?dVc>FM;4%g>;|(u1#clKs6JaG?-nw)1iqC1ikQ4MgC+K-j(DR&jG^h1KPSD!} z3pt^`J&-wZr?&TRZ%C;0m$WKgROJO#;-Dm|;OC1UmAtJn*R@9#d0%@}k$3#Txa(Kq zD=+SOabJd!5ZhT`no3!!0}$tNZ29v9H>9lLxUb;~Uo&a+g%QJJi*5<;z)>O`gaJ9u zK`(;je0zVep$N-_);f*c+UT%0yDU!0aoR3GAk09X4VU@UWBMI_Qf5&Rk_ z{c~CJxp1s<^^)Ap2xtO6b6V>i)<$kQ1&=2Kuz{ypbrob~kzE4WLUy3UZ}i0AD^u#b zS~!es9It%+B+ zp%9qA#<~{N0jd?VGY5Za*v&@2#usI09%}hK)be?#wdwe1XNH~ix`4k#0YG~DD++!Lw`58D#wAJZvH-a>|gLW z{QYnqzDL0f;{H$Qhkh3G5Elb3K@v{}JOz1@cp7{siD$rflDG`Mlf)J9oh0<*I1gSz zKOTC4>*cu!Uq3Y9D`E+DKPCRO_7O>fq{Y1fq{V?NV7IDFt`F~83zVNIfe!XW*~d=O;%|QAk6?)2&5T- Lm1Mg z?CYb)55avt zgqUoYI_L`$QYyU-v+`+DyJ>>%kzFk3fvHT4?fb&JTW@28E=Vv)%f0iyffi3Qm&V&H sZZavqG)w=+2n-2z0HW1G40j>QEkscuzq=24Ul*t3_8~vg1*y&Y1&47oy8r+H literal 0 HcmV?d00001