From 087ba0d81dfba6658136c6f744fcebaf4aaba8f0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 17 May 2024 22:33:57 +0800 Subject: [PATCH] mv x/ => c/ --- .gitmodules | 3 - README.md | 10 +- _demo/llama2-c/run.go | 2 +- {x => c}/cjson/README.md | 0 {x => c}/cjson/_demo/mkjson/mkjson.go | 2 +- {x => c}/cjson/cjson.go | 0 c/cjson/llgo_autogen.lla | Bin 0 -> 400 bytes {x => c}/llama2/.gitignore | 0 c/llama2/llama2.go | 159 ++++++++++++++ {x => c}/llama2/llama2/run.c | 0 {x => c}/llama2/llgo.cfg | 2 +- {x => c}/llama2/llgo_autogen.lla | Bin {x => c}/sqlite/README.md | 0 {x => c}/sqlite/_demo/sqlitedemo/demo.go | 2 +- c/sqlite/llgo_autogen.lla | Bin 0 -> 852 bytes {x => c}/sqlite/sqlite.go | 58 +++-- x/cjson/llgo_autogen.lla | Bin 401 -> 0 bytes x/llama2/llama2.c | 1 - x/llama2/llama2.go | 268 ----------------------- x/sqlite/llgo_autogen.lla | Bin 852 -> 0 bytes 20 files changed, 195 insertions(+), 312 deletions(-) rename {x => c}/cjson/README.md (100%) rename {x => c}/cjson/_demo/mkjson/mkjson.go (93%) rename {x => c}/cjson/cjson.go (100%) create mode 100644 c/cjson/llgo_autogen.lla rename {x => c}/llama2/.gitignore (100%) create mode 100644 c/llama2/llama2.go rename {x => c}/llama2/llama2/run.c (100%) rename {x => c}/llama2/llgo.cfg (56%) rename {x => c}/llama2/llgo_autogen.lla (100%) rename {x => c}/sqlite/README.md (100%) rename {x => c}/sqlite/_demo/sqlitedemo/demo.go (97%) create mode 100644 c/sqlite/llgo_autogen.lla rename {x => c}/sqlite/sqlite.go (82%) delete mode 100644 x/cjson/llgo_autogen.lla delete mode 160000 x/llama2/llama2.c delete mode 100644 x/llama2/llama2.go delete mode 100644 x/sqlite/llgo_autogen.lla diff --git a/.gitmodules b/.gitmodules index 0f3ceb95..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "x/llama2/llama2.c"] - path = x/llama2/llama2.c - url = https://github.com/karpathy/llama2.c.git diff --git a/README.md b/README.md index e22c3bc0..b58bbb0b 100644 --- a/README.md +++ b/README.md @@ -150,15 +150,15 @@ LLGo can easily import any libraries from the C ecosystem. Currently, this impor The currently imported libraries include: -* [llama2.c](https://pkg.go.dev/github.com/goplus/llgo/x/llama2) -* [cjson](https://pkg.go.dev/github.com/goplus/llgo/x/cjson) -* [sqlite](https://pkg.go.dev/github.com/goplus/llgo/x/sqlite) +* [llama2.c](https://pkg.go.dev/github.com/goplus/llgo/c/llama2) +* [cjson](https://pkg.go.dev/github.com/goplus/llgo/c/cjson) +* [sqlite](https://pkg.go.dev/github.com/goplus/llgo/c/sqlite) Here are some examples related to them: * [llama2-c](_demo/llama2-c): inference Llama 2 (It's the first llgo AI example) -* [mkjson](x/cjson/_demo/mkjson/mkjson.go): create a json object and print it -* [sqlitedemo](x/sqlite/_demo/sqlitedemo/demo.go): a basic sqlite demo +* [mkjson](c/cjson/_demo/mkjson/mkjson.go): create a json object and print it +* [sqlitedemo](c/sqlite/_demo/sqlitedemo/demo.go): a basic sqlite demo ## Go syntax support diff --git a/_demo/llama2-c/run.go b/_demo/llama2-c/run.go index af6acb90..d7b2102a 100644 --- a/_demo/llama2-c/run.go +++ b/_demo/llama2-c/run.go @@ -2,7 +2,7 @@ package main import ( "github.com/goplus/llgo/c" - "github.com/goplus/llgo/x/llama2" + "github.com/goplus/llgo/c/llama2" ) func main() { diff --git a/x/cjson/README.md b/c/cjson/README.md similarity index 100% rename from x/cjson/README.md rename to c/cjson/README.md diff --git a/x/cjson/_demo/mkjson/mkjson.go b/c/cjson/_demo/mkjson/mkjson.go similarity index 93% rename from x/cjson/_demo/mkjson/mkjson.go rename to c/cjson/_demo/mkjson/mkjson.go index 68e08649..e91db71f 100644 --- a/x/cjson/_demo/mkjson/mkjson.go +++ b/c/cjson/_demo/mkjson/mkjson.go @@ -2,7 +2,7 @@ package main import ( "github.com/goplus/llgo/c" - "github.com/goplus/llgo/x/cjson" + "github.com/goplus/llgo/c/cjson" ) func main() { diff --git a/x/cjson/cjson.go b/c/cjson/cjson.go similarity index 100% rename from x/cjson/cjson.go rename to c/cjson/cjson.go diff --git a/c/cjson/llgo_autogen.lla b/c/cjson/llgo_autogen.lla new file mode 100644 index 0000000000000000000000000000000000000000..68eceb56b7aee51a381575d16a8bd1c990fb0fc7 GIT binary patch literal 400 zcmWIWW@Zs#U|`^2u->#W!jb!&#~mOql8J$VpFxHpCnr5WKC!eUKRq>1FDEB7gp+|; zDBV3x1c*y3xEUB(zA`c}fDKqW@wDGz0|D3XpG6mUtWt3Gy_Bl0B9;^JMOsOHp48%% z>Yti@kM1|F^*q4T^=j_gozLYeuk8?wo)I_iqu}#%&#xQlxZO1MQ`1|SSazxB?2lE6;9}NMAnfX@52Q^Iv(b z;qy)XCgucPHC}r4&=l263yqIEneVQjzRyE1f8rJXzl)?b<;7X$KX3+kGcw6B;|eAT nV1O|&0K%dL#+ literal 0 HcmV?d00001 diff --git a/x/llama2/.gitignore b/c/llama2/.gitignore similarity index 100% rename from x/llama2/.gitignore rename to c/llama2/.gitignore diff --git a/c/llama2/llama2.go b/c/llama2/llama2.go new file mode 100644 index 00000000..f3067cb3 --- /dev/null +++ b/c/llama2/llama2.go @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package llama2 + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +const ( + LLGoPackage = "link" +) + +// ----------------------------------------------------------------------------- + +// llgo:type C +type TokenIndex struct { + Str *c.Char + Id c.Int +} + +// llgo:type C +type Tokenizer struct { + Vocab **c.Char + VocabScores *c.Float + SortedVocab *TokenIndex + VocabSize c.Int + MaxTokenLength c.Uint + BytePieces [512]uint8 // stores all single-byte strings +} + +//go:linkname BuildTokenizer C.build_tokenizer +func BuildTokenizer(t *Tokenizer, tokenizerPath *c.Char, vocabSize c.Int) + +//go:linkname FreeTokenizer C.free_tokenizer +func FreeTokenizer(t *Tokenizer) + +// ----------------------------------------------------------------------------- + +// llgo:type C +type Config struct { + Dim c.Int // transformer dimension + HiddenDim c.Int // for ffn layers + NLayers c.Int // number of layers + NHeads c.Int // number of query heads + NKVHeads c.Int // number of key/value heads (can be < query heads because of multiquery) + VocabSize c.Int // vocabulary size, usually 256 (byte-level) + SeqLen c.Int // max sequence length +} + +// llgo:type C +type TransformerWeights struct { + // token embedding table + TokenEmbeddingTable *c.Float // (vocab_size, dim) + // weights for rmsnorms + RmsAttWeight *c.Float // (layer, dim) rmsnorm weights + RmsFfnWeight *c.Float // (layer, dim) + // weights for matmuls. note dim == n_heads * head_size + Wq *c.Float // (layer, dim, n_heads * head_size) + Wk *c.Float // (layer, dim, n_kv_heads * head_size) + Wv *c.Float // (layer, dim, n_kv_heads * head_size) + Wo *c.Float // (layer, n_heads * head_size, dim) + // weights for ffn + W1 *c.Float // (layer, hidden_dim, dim) + W2 *c.Float // (layer, dim, hidden_dim) + W3 *c.Float // (layer, hidden_dim, dim) + // final rmsnorm + RmsFinalWeight *c.Float // (dim,) + // (optional) classifier weights for the logits, on the last layer + Wcls *c.Float +} + +// llgo:type C +type RunState struct { + // current wave of activations + X *c.Float // activation at current time stamp (dim,) + Xb *c.Float // same, but inside a residual branch (dim,) + Xb2 *c.Float // an additional buffer just for convenience (dim,) + Hb *c.Float // buffer for hidden dimension in the ffn (hidden_dim,) + Hb2 *c.Float // buffer for hidden dimension in the ffn (hidden_dim,) + Q *c.Float // query (dim,) + K *c.Float // key (dim,) + V *c.Float // value (dim,) + Att *c.Float // buffer for scores/attention values (n_heads, seq_len) + Logits *c.Float // output logits + // kv cache + KeyCache *c.Float // (layer, seq_len, dim) + ValueCache *c.Float // (layer, seq_len, dim) +} + +// llgo:type C +type Transformer struct { + Config Config // the hyperparameters of the architecture (the blueprint) + Weights TransformerWeights // the weights of the model + State RunState // buffers for the "wave" of activations in the forward pass + + // some more state needed to properly clean up the memory mapping (sigh) + Fd c.Int // file descriptor for memory mapping + Data *c.Float // memory mapped data pointer + FileSize uintptr // size of the checkpoint file in bytes +} + +//go:linkname BuildTransformer C.build_transformer +func BuildTransformer(t *Transformer, checkpointPath *c.Char) + +//go:linkname FreeTransformer C.free_transformer +func FreeTransformer(t *Transformer) + +// ----------------------------------------------------------------------------- + +// llgo:type C +type ProbIndex struct { + Prob c.Float + Index c.Int +} // struct used when sorting probabilities during top-p sampling + +// llgo:type C +type Sampler struct { + VocabSize c.Int + Probindex *ProbIndex // buffer used in top-p sampling + Temperature c.Float + Topp c.Float + RngState uint64 +} + +//go:linkname BuildSampler C.build_sampler +func BuildSampler(sampler *Sampler, vocabSize c.Int, temperature c.Float, topp c.Float, rngSeed uint64) + +//go:linkname FreeSampler C.free_sampler +func FreeSampler(sampler *Sampler) + +// ----------------------------------------------------------------------------- + +//go:linkname Generate C.generate +func Generate( + transformer *Transformer, tokenizer *Tokenizer, sampler *Sampler, + prompt *c.Char, steps c.Int) + +//go:linkname Chat C.chat +func Chat( + transformer *Transformer, tokenizer *Tokenizer, sampler *Sampler, + cliUserPrompt *c.Char, cliSystemPrompt *c.Char, steps c.Int) + +// ----------------------------------------------------------------------------- diff --git a/x/llama2/llama2/run.c b/c/llama2/llama2/run.c similarity index 100% rename from x/llama2/llama2/run.c rename to c/llama2/llama2/run.c diff --git a/x/llama2/llgo.cfg b/c/llama2/llgo.cfg similarity index 56% rename from x/llama2/llgo.cfg rename to c/llama2/llgo.cfg index 0b74636c..5100faf1 100644 --- a/x/llama2/llgo.cfg +++ b/c/llama2/llgo.cfg @@ -1,6 +1,6 @@ { "cl": [ "clang -emit-llvm -S -o llgo_autogen.ll -c llama2/run.c", - "zip llgo_autogen.lla llgo_autogen.ll" + "rm llgo_autogen.lla; zip llgo_autogen.lla llgo_autogen.ll" ] } diff --git a/x/llama2/llgo_autogen.lla b/c/llama2/llgo_autogen.lla similarity index 100% rename from x/llama2/llgo_autogen.lla rename to c/llama2/llgo_autogen.lla diff --git a/x/sqlite/README.md b/c/sqlite/README.md similarity index 100% rename from x/sqlite/README.md rename to c/sqlite/README.md diff --git a/x/sqlite/_demo/sqlitedemo/demo.go b/c/sqlite/_demo/sqlitedemo/demo.go similarity index 97% rename from x/sqlite/_demo/sqlitedemo/demo.go rename to c/sqlite/_demo/sqlitedemo/demo.go index 9dc4dc0f..a8d0deaf 100644 --- a/x/sqlite/_demo/sqlitedemo/demo.go +++ b/c/sqlite/_demo/sqlitedemo/demo.go @@ -2,7 +2,7 @@ package main import ( "github.com/goplus/llgo/c" - "github.com/goplus/llgo/x/sqlite" + "github.com/goplus/llgo/c/sqlite" ) func main() { diff --git a/c/sqlite/llgo_autogen.lla b/c/sqlite/llgo_autogen.lla new file mode 100644 index 0000000000000000000000000000000000000000..becb6e8037a736718982368927d8f0137f268d55 GIT binary patch literal 852 zcmWIWW@Zs#U|`^2u-&{dV$0m0G7Fd(7-IMt82A}v7;oWNbznjdxOv$s6EYtNloQ2jcJaN(g$%|QOGF^@>&TdXNiIxvD zc#of-qd4isBAx%!ojz~{opWP2Tk~OAW_R$~V;u_r6i+&fofhI;wp!q{P{R|6>?odi zfySf!+;{&}thyC`jAQGQ=wmbUPMI9uc3}0kDTUs@9hnalxG4J^mQXk^%P4Bhr0B-U zxR0m6)j=ewHF=>|wRePwsp}+-#7+ygou|sSZkNh5-QQ8gy!?2*m!f4`G56x_3iAz@ zUDUrjMTjLVTds4}y!NhZX{NOw<9t#pL~V~MymE5&S75%DqwFMbQsMDzf#-7<26q{G zo$|?Nd1G|ka+(VBm2~HfM^oOsYq7X)`1kVF*PT=QRDHIe-fJ%M{h#Bz>C)SM_itzR z5}Wbv)WcYv<6mkl|D1keQM=wwTW`%rezwaK4z4wMu0LOzlk$A(`7I}VV;g(3qOHW7_XmHjwYd=^@Lq3E zBX{|<2fGEQ3hvGBjrcoVYtpW^FYQK89Mm>x3-hL)sJuO4bDLuG@6TgARZ_*yfp;EnQ1O4d>!xXMto)qQuI1N0?px&iD8?{w@dJr= z?T*HUw|2~Z-xv0+T+ZSBb!CB|V>1s#g-wh}n`5L=&E_W|7#M(QnPEvIh=q~xSs@7@Ezt*fv$BDdGXh~KkbViwI}8i}<9=)P literal 0 HcmV?d00001 diff --git a/x/sqlite/sqlite.go b/c/sqlite/sqlite.go similarity index 82% rename from x/sqlite/sqlite.go rename to c/sqlite/sqlite.go index 133a5b8f..dcf4a86b 100644 --- a/x/sqlite/sqlite.go +++ b/c/sqlite/sqlite.go @@ -22,12 +22,6 @@ import ( "github.com/goplus/llgo/c" ) -type ( - Char = c.Char - Int = c.Int - Pointer = c.Pointer -) - const ( LLGoPackage = "link: sqlite3" ) @@ -44,7 +38,7 @@ type Stmt struct { // ----------------------------------------------------------------------------- -type Errno Int +type Errno c.Int const ( OK Errno = 0 // Successful result @@ -83,10 +77,10 @@ const ( ) // llgo:link (Errno).Errstr C.sqlite3_errstr -func (err Errno) Errstr() *Char { return nil } +func (err Errno) Errstr() *c.Char { return nil } // llgo:link (*Sqlite3).Errmsg C.sqlite3_errmsg -func (db *Sqlite3) Errmsg() *Char { return nil } +func (db *Sqlite3) Errmsg() *c.Char { return nil } // llgo:link (*Sqlite3).Errcode C.sqlite3_errcode func (db *Sqlite3) Errcode() Errno { return 0 } @@ -97,13 +91,13 @@ func (db *Sqlite3) ExtendedErrcode() Errno { return 0 } // ----------------------------------------------------------------------------- //go:linkname doOpen C.sqlite3_open -func doOpen(filename *Char, ppDb **Sqlite3) Errno +func doOpen(filename *c.Char, ppDb **Sqlite3) Errno //go:linkname doOpenV2 C.sqlite3_open_v2 -func doOpenV2(filename *Char, ppDb **Sqlite3, flags OpenFlags, zVfs *Char) Errno +func doOpenV2(filename *c.Char, ppDb **Sqlite3, flags OpenFlags, zVfs *c.Char) Errno // OpenFlags represents SQLite open flags. -type OpenFlags Int +type OpenFlags c.Int const ( OpenReadOnly OpenFlags = 0x00000001 @@ -132,7 +126,7 @@ const ( // Opening A New Database Connection // filename: Database filename (UTF-8) -func Open(filename *Char) (db *Sqlite3, err Errno) { +func Open(filename *c.Char) (db *Sqlite3, err Errno) { err = doOpen(filename, &db) return } @@ -140,7 +134,7 @@ func Open(filename *Char) (db *Sqlite3, err Errno) { // Opening A New Database Connection // filename: Database filename (UTF-8) // zVfs: Name of VFS module to use -func OpenV2(filename *Char, flags OpenFlags, zVfs *Char) (db *Sqlite3, err Errno) { +func OpenV2(filename *c.Char, flags OpenFlags, zVfs *c.Char) (db *Sqlite3, err Errno) { err = doOpenV2(filename, &db, flags, zVfs) return } @@ -158,22 +152,22 @@ func (db *Sqlite3) CloseV2() Errno { return 0 } // ----------------------------------------------------------------------------- // llgo:link (*Sqlite3).doPrepare C.sqlite3_prepare -func (*Sqlite3) doPrepare(*Char, Int, **Stmt, **Char) Errno { +func (*Sqlite3) doPrepare(*c.Char, c.Int, **Stmt, **c.Char) Errno { return 0 } // llgo:link (*Sqlite3).doPrepareV2 C.sqlite3_prepare_v2 -func (*Sqlite3) doPrepareV2(*Char, Int, **Stmt, **Char) Errno { +func (*Sqlite3) doPrepareV2(*c.Char, c.Int, **Stmt, **c.Char) Errno { return 0 } // llgo:link (*Sqlite3).doPrepareV3 C.sqlite3_prepare_v3 -func (*Sqlite3) doPrepareV3(*Char, Int, PrepareFlags, **Stmt, **Char) Errno { +func (*Sqlite3) doPrepareV3(*c.Char, c.Int, PrepareFlags, **Stmt, **c.Char) Errno { return 0 } // PrepareFlags represents SQLite prepare flags. -type PrepareFlags Int +type PrepareFlags c.Int const ( PreparePersistent PrepareFlags = 0x01 @@ -183,17 +177,17 @@ const ( // Compiling An SQL Statement // tail: Pointer to unused portion of sql -func (db *Sqlite3) Prepare(sql string, tail **Char) (stmt *Stmt, err Errno) { +func (db *Sqlite3) Prepare(sql string, tail **c.Char) (stmt *Stmt, err Errno) { err = db.doPrepare(c.GoStringData(sql), c.Int(len(sql)), &stmt, tail) return } -func (db *Sqlite3) PrepareV2(sql string, tail **Char) (stmt *Stmt, err Errno) { +func (db *Sqlite3) PrepareV2(sql string, tail **c.Char) (stmt *Stmt, err Errno) { err = db.doPrepareV2(c.GoStringData(sql), c.Int(len(sql)), &stmt, tail) return } -func (db *Sqlite3) PrepareV3(sql string, flags PrepareFlags, tail **Char) (stmt *Stmt, err Errno) { +func (db *Sqlite3) PrepareV3(sql string, flags PrepareFlags, tail **c.Char) (stmt *Stmt, err Errno) { err = db.doPrepareV3(c.GoStringData(sql), c.Int(len(sql)), flags, &stmt, tail) return } @@ -206,10 +200,10 @@ func (stmt *Stmt) Close() Errno { return 0 } // ----------------------------------------------------------------------------- // llgo:link (*Stmt).BindInt C.sqlite3_bind_int -func (*Stmt) BindInt(idx Int, val Int) Errno { return 0 } +func (*Stmt) BindInt(idx c.Int, val c.Int) Errno { return 0 } // llgo:link (*Stmt).BindInt64 C.sqlite3_bind_int64 -func (*Stmt) BindInt64(idx Int, val int64) Errno { return 0 } +func (*Stmt) BindInt64(idx c.Int, val int64) Errno { return 0 } /* const ( @@ -219,7 +213,9 @@ const ( */ // llgo:link (*Stmt).BindText C.sqlite3_bind_text -func (*Stmt) BindText(idx Int, val *Char, nByte Int, destructor func(Pointer)) Errno { return 0 } +func (*Stmt) BindText(idx c.Int, val *c.Char, nByte c.Int, destructor func(c.Pointer)) Errno { + return 0 +} // ----------------------------------------------------------------------------- @@ -238,19 +234,19 @@ func (*Stmt) Step() Errno { return 0 } // ----------------------------------------------------------------------------- // llgo:link (*Stmt).ColumnCount C.sqlite3_column_count -func (stmt *Stmt) ColumnCount() Int { return 0 } +func (stmt *Stmt) ColumnCount() c.Int { return 0 } // llgo:link (*Stmt).ColumnName C.sqlite3_column_name -func (stmt *Stmt) ColumnName(idx Int) *Char { return nil } +func (stmt *Stmt) ColumnName(idx c.Int) *c.Char { return nil } // llgo:link (*Stmt).ColumnInt C.sqlite3_column_int -func (stmt *Stmt) ColumnInt(idx Int) Int { return 0 } +func (stmt *Stmt) ColumnInt(idx c.Int) c.Int { return 0 } // llgo:link (*Stmt).ColumnInt64 C.sqlite3_column_int64 -func (stmt *Stmt) ColumnInt64(idx Int) int64 { return 0 } +func (stmt *Stmt) ColumnInt64(idx c.Int) int64 { return 0 } // llgo:link (*Stmt).ColumnText C.sqlite3_column_text -func (stmt *Stmt) ColumnText(idx Int) *Char { return nil } +func (stmt *Stmt) ColumnText(idx c.Int) *c.Char { return nil } // ----------------------------------------------------------------------------- @@ -258,8 +254,8 @@ func (stmt *Stmt) ColumnText(idx Int) *Char { return nil } // // llgo:link (*Sqlite3).Exec C.sqlite3_exec func (*Sqlite3) Exec( - sql *Char, callback func(arg Pointer, resultCols Int, colVals, colNames **Char) Int, - arg Pointer, errmsg **Char) Errno { + sql *c.Char, callback func(arg c.Pointer, resultCols c.Int, colVals, colNames **c.Char) c.Int, + arg c.Pointer, errmsg **c.Char) Errno { return 0 } diff --git a/x/cjson/llgo_autogen.lla b/x/cjson/llgo_autogen.lla deleted file mode 100644 index a5c17d01bb16f9eb440eeebec6c20a3e95984d5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 401 zcmWIWW@Zs#U|`^2*cH1jBD?2e@LeD;l8J$VpFxHpCnr5WKC!eUKRq>1FDEB7gp+|e z|DID?0T7o~a5FHnd}U-{02{D$qOUizp+MXF&)RKEmvVWRZ$B<}uz9-NA71Y=<6h0n zXD0Cr9{K-nOIQGZRAwIMXS?^#Y0dpf+?T&GzAt!KQ>S&fXo~aWlt+`K($vp-8^4>} z!m6TKxTNm+%qGhTZmaGqiyrtZuk0;fGjC_oqO~F~BG?XFJndL$z#MVwr^64Odyib# zym}@UztDc)_qS34&({`3_PE}^s{W~dPL|1=*aLo^&LJ(IvQmr%9{*XK`t|X>gFE*{ zKR%k?9IP@s