Posted in

Go并发编程实战:3个被忽视的goroutine泄漏陷阱及紧急修复方案

第一章:Go并发编程实战:3个被忽视的goroutine泄漏陷阱及紧急修复方案

goroutine泄漏是Go服务长期运行后内存持续增长、响应变慢甚至OOM的核心诱因。许多开发者仅关注go关键字的显式调用,却忽略了隐式启动、上下文失效与通道阻塞带来的“静默泄漏”。以下三个高频陷阱常被忽略,且具备明确可验证的修复路径。

未关闭的HTTP服务器监听器

当使用http.ListenAndServe后未配合Shutdown主动终止,底层监听goroutine将持续驻留。修复需引入context控制生命周期:

srv := &http.Server{Addr: ":8080", Handler: mux}
go func() {
    if err := srv.ListenAndServe(); err != http.ErrServerClosed {
        log.Fatal(err)
    }
}()
// 优雅关闭(如收到SIGTERM时)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
    log.Fatal("Server shutdown failed:", err)
}

无缓冲通道的单向发送阻塞

向无缓冲channel发送数据而无对应接收者,会永久阻塞goroutine。常见于日志采集、指标上报等异步场景:

// ❌ 危险:若logCh无人接收,goroutine永不退出
go func() { logCh <- "event" }()

// ✅ 修复:使用带缓冲channel + select超时兜底
logCh := make(chan string, 100) // 缓冲区防阻塞
go func() {
    select {
    case logCh <- "event":
    default:
        // 丢弃或降级处理,避免goroutine卡死
        log.Warn("log channel full, dropped")
    }
}()

Context取消后未清理子goroutine

父context取消后,子goroutine若未监听ctx.Done(),将脱离生命周期管理:

场景 风险表现 修复要点
time.AfterFunc 定时器触发后goroutine残留 改用time.AfterFunc + ctx检查
for range chan channel关闭后循环仍运行 循环内添加select{case <-ctx.Done(): return}

务必在所有goroutine入口处校验ctx.Err(),并确保资源释放逻辑位于defer或显式清理块中。

第二章:陷阱一:未关闭的channel导致goroutine永久阻塞

2.1 channel底层模型与goroutine生命周期耦合机制

channel并非独立的通信管道,而是与goroutine调度深度绑定的同步原语。其底层由hchan结构体承载,内含锁、等待队列(sendq/recvq)及环形缓冲区。

数据同步机制

当goroutine在channel上阻塞时,会被封装为sudog节点挂入对应等待队列,并主动让出M/P,进入Gwaiting状态——此时goroutine生命周期被runtime接管。

// runtime/chan.go 简化示意
type hchan struct {
    lock      mutex
    sendq     waitq  // 阻塞的发送goroutine链表
    recvq     waitq  // 阻塞的接收goroutine链表
    qcount    uint   // 当前缓冲区元素数
    dataqsiz  uint   // 缓冲区容量
    buf       unsafe.Pointer // 环形缓冲区起始地址
}

buf指向动态分配的连续内存;qcountdataqsiz共同决定是否触发阻塞;sendq/recvq是双向链表,由g指针构成,实现goroutine唤醒时的精准调度。

调度耦合关键点

  • goroutine入队即暂停执行,不消耗CPU;
  • 唤醒时通过goready()将其置为Grunnable,交由调度器重新分配P;
  • 若channel关闭,所有等待中的goroutine立即被唤醒并返回零值或panic。
事件 goroutine状态变迁 调度介入时机
ch Grunning → Gwaiting gopark()
Grunning → Gwaiting gopark()
对方就绪并完成操作 Gwaiting → Grunnable goready()
graph TD
    A[goroutine执行send] --> B{channel可写?}
    B -- 是 --> C[写入buf/唤醒recvq头]
    B -- 否 --> D[构造sudog→入sendq→gopark]
    D --> E[等待被recv goroutine唤醒]
    E --> F[goready→重新入调度队列]

2.2 复现案例:select + unbuffered channel 的死锁场景

死锁触发条件

select 语句仅包含对无缓冲 channel 的发送或接收操作,且无 default 分支时,若无协程同步配合,立即阻塞并永久等待。

复现代码

func main() {
    ch := make(chan int) // 无缓冲 channel
    select {
    case ch <- 42: // 永远阻塞:无人接收
        fmt.Println("sent")
    }
}

逻辑分析:ch 无缓冲,发送需等待另一 goroutine 同步接收;但主 goroutine 单线程执行 select 后无其他协程启动,导致所有 case 永久不可达,运行时 panic "fatal error: all goroutines are asleep - deadlock!"

关键参数说明

  • make(chan int):容量为 0,收发必须成对阻塞同步
  • selectdefault:放弃非阻塞兜底,强制等待可执行 case
场景 是否死锁 原因
send to unbuffered 无接收者,发送永远挂起
receive from buffered 缓冲区有值即可立即返回

2.3 pprof + go tool trace 定位阻塞goroutine的实操路径

当服务响应延迟突增,runtime.GoroutineProfile 显示大量 syscallchan receive 状态 goroutine 时,需结合双工具交叉验证。

启动性能采集

# 同时启用 block profile(需显式设置)和 trace
go run -gcflags="-l" main.go &
PID=$!
sleep 5
curl "http://localhost:6060/debug/pprof/block?debug=1" > block.out
curl "http://localhost:6060/debug/pprof/trace?seconds=5" > trace.out

block profile 需在程序启动前设置 GODEBUG=gctrace=1 并调用 runtime.SetBlockProfileRate(1)trace 默认捕获调度、网络、GC 等全事件流。

分析阻塞热点

go tool trace trace.out
# 在 Web UI 中点击 "Goroutines" → "View trace" → 拖拽定位长时间处于 "Waiting" 状态的 goroutine

关键指标对照表

工具 擅长定位 采样开销 阻塞类型示例
pprof/block 互斥锁、channel 等待 sync.Mutex.Lock
go tool trace 调度延迟、系统调用阻塞 read on socket, futex

典型阻塞链路

graph TD
    A[HTTP Handler] --> B[DB Query]
    B --> C[net.Conn.Read]
    C --> D[syscall.Syscall]
    D --> E[OS Scheduler Wait]

go tool trace 可直观看到 E→D 的等待时长,而 block profile 会将该路径聚合为 net.(*conn).Read 的 block event。

2.4 修复模式:done channel + context.WithCancel 的标准封装

在高并发任务中,需兼顾优雅终止与资源可取消性。done channel 提供信号通知,context.WithCancel 提供生命周期管理,二者组合构成健壮的修复入口。

核心封装模式

func RunRepair(ctx context.Context, work func(ctx context.Context) error) error {
    done := make(chan struct{})
    go func() {
        defer close(done)
        _ = work(ctx) // work 内部需监听 ctx.Done()
    }()

    select {
    case <-done:
        return nil
    case <-ctx.Done():
        return ctx.Err() // 如 context.Canceled 或 DeadlineExceeded
    }
}

逻辑分析:done 通道仅用于同步 goroutine 完成;work 函数必须主动响应 ctx.Done()(如用 select { case <-ctx.Done(): return }),否则无法及时中断;RunRepair 自身不持有 cancel 函数,职责清晰。

关键对比

特性 单纯 done channel context.WithCancel 封装
超时控制 ✅(配合 WithTimeout)
可嵌套取消传播
错误类型语义明确性 需手动约定 原生 context.Canceled

使用约束

  • work 函数不得忽略 ctx 参数;
  • done 通道必须由工作 goroutine 关闭,避免 panic;
  • 外部调用者应通过 context.WithCancel 创建可主动取消的上下文。

2.5 单元测试验证:使用runtime.NumGoroutine() 捕获泄漏回归

Goroutine 泄漏常表现为协程数在操作前后持续增长,runtime.NumGoroutine() 提供轻量级快照能力,适合在测试前后断言。

测试模式:差值断言

func TestConcurrentProcessor(t *testing.T) {
    before := runtime.NumGoroutine()
    p := NewProcessor()
    p.Start() // 启动后台监听
    time.Sleep(10 * time.Millisecond)
    p.Stop() // 应确保所有 goroutine 退出
    after := runtime.NumGoroutine()
    if after-before > 0 {
        t.Fatalf("goroutine leak detected: +%d", after-before)
    }
}

逻辑分析:before/after 在同一调度器上下文中采样,差值 > 0 表明存在未回收的 goroutine;Sleep 为让异步逻辑完成,实际应配合 sync.WaitGroupcontext 更精确等待。

常见误判场景对比

场景 NumGoroutine 变化 是否真实泄漏
GC 未及时触发 +1~2(短暂)
日志/监控 goroutine 持久运行 +1(稳定) 是(需设计可关闭)
time.AfterFunc 未清理 +1(永久)

防御性增强策略

  • 结合 pprof.GoroutineProfile 获取堆栈定位源头
  • TestMain 中注入全局 goroutine 计数基线校验

第三章:陷阱二:Timer/Ticker未显式停止引发的隐式泄漏

3.1 Timer和Ticker在GC视角下的资源持有关系解析

Go 运行时中,*time.Timer*time.Ticker 均持有一个指向内部 timer 结构的指针,该结构被全局 timer heap 引用,阻止其被 GC 回收,即使用户已丢弃变量引用。

GC 可达性关键路径

  • Timer.Cchan Time)是 runtime.timer 的唯一强引用出口
  • Ticker.C 同理,但其底层 timer 被周期性重置,持续驻留堆中

内存泄漏典型场景

func leakyTimer() {
    t := time.NewTimer(1 * time.Hour)
    // 忘记调用 t.Stop() → timer 结构永不释放
    <-t.C // 即使 channel 已读,timer 仍注册在全局 heap 中
}

t.Stop() 返回 true 表示 timer 尚未触发且成功移除;若返回 false,说明已触发或已停止,此时无需额外操作。未调用 Stop() 将导致 timer 永久挂载于 netpoll 相关数据结构中,GC 不可达。

对象类型 是否可被 GC 触发条件
Timer 否(若未 Stop) timer 结构仍在 heap 中
Ticker 否(若未 Stop) 每次 reset 重建 timer
graph TD
    A[用户创建 Timer] --> B[runtime.timer 加入全局 heap]
    B --> C[GC 标记阶段:heap 为根,timer 可达]
    C --> D[timer 不回收]
    D --> E[必须显式 Stop 才能从 heap 移除]

3.2 真实业务代码片段:HTTP长轮询中ticker忘记Stop的典型误用

数据同步机制

某实时日志推送服务采用 HTTP 长轮询 + time.Ticker 实现心跳探测,客户端每 30 秒拉取一次变更。

func handleLongPoll(w http.ResponseWriter, r *http.Request) {
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop() // ❌ 错误:defer 在函数返回时才执行,但 goroutine 可能长期存活

    go func() {
        for range ticker.C {
            select {
            case <-r.Context().Done(): // 客户端断开
                return
            default:
                // 推送增量数据...
            }
        }
    }()
}

逻辑分析ticker 在 goroutine 中持续触发,但 defer ticker.Stop() 绑定在 handler 函数栈上,而该函数早已返回;goroutine 持有 ticker 引用导致其无法被 GC,内存与 timer 资源持续泄漏。

修复方案对比

方案 是否释放 ticker 是否响应上下文取消
defer ticker.Stop()(主函数内) ✅ 否(goroutine 外) ❌ 不感知 r.Context()
select { case <-ticker.C: ... case <-r.Context().Done(): ticker.Stop(); return } ✅ 是 ✅ 是
graph TD
    A[Client发起长轮询] --> B[启动ticker]
    B --> C{是否收到Cancel?}
    C -->|是| D[ticker.Stop()]
    C -->|否| E[继续推送]
    D --> F[goroutine退出]

3.3 修复实践:defer timer.Stop() 的安全边界与recover兜底策略

timer.Stop() 并非幂等操作,重复调用可能引发 panic(如 timer 已停止或已触发)。需严格限定其执行前提。

安全调用条件

  • timer 必须处于 CreatedActive 状态;
  • 不可在 Stop() 后再次调用,也不可在 Reset()/Stop() 并发调用;
// 正确:使用原子状态标记避免重复 Stop
var stopped atomic.Bool
defer func() {
    if !stopped.Swap(true) && !t.Stop() {
        // timer 已触发,需 Drain channel 防 goroutine 泄漏
        select {
        case <-t.C:
        default:
        }
    }
}()

t.Stop() 返回 false 表示 timer 已触发或已停止,此时必须手动消费 t.C,否则泄漏 goroutine。

recover 兜底场景

场景 是否适用 recover 说明
Stop 已在 defer 中 panic 可捕获,但属设计缺陷
timer.C 关闭后读取 panic 不可 recover(send on closed channel)
graph TD
    A[启动 timer] --> B{timer.Stop 调用}
    B -->|成功| C[释放资源]
    B -->|失败| D[检查 t.C 是否可接收]
    D --> E[select 消费或丢弃]

第四章:陷阱三:闭包捕获外部变量导致goroutine无法被GC回收

4.1 Go逃逸分析与闭包变量生命周期的深度关联

Go 编译器通过逃逸分析决定变量分配在栈还是堆,而闭包捕获的变量会因引用关系强制延长生命周期。

闭包触发逃逸的典型场景

func makeAdder(x int) func(int) int {
    return func(y int) int { return x + y } // x 逃逸至堆
}

x 原本是 makeAdder 栈帧局部变量,但被返回的闭包函数值捕获并可能在调用方作用域长期存活,编译器判定其必须分配在堆上。

关键判定逻辑

  • 若闭包函数值被返回或赋值给包级变量 → 捕获变量逃逸
  • 若闭包仅在当前函数内调用且无外部引用 → 变量可保留在栈
场景 x 是否逃逸 原因
return func() { return x } ✅ 是 闭包值脱离当前栈帧生存期
f := func() { return x }; f() ❌ 否(通常) 无跨栈帧传递,x 可栈分配
graph TD
    A[定义闭包] --> B{是否返回/导出?}
    B -->|是| C[捕获变量逃逸至堆]
    B -->|否| D[变量可栈分配]

4.2 案例复现:for循环中启动goroutine并引用循环变量i的内存陷阱

问题代码示例

for i := 0; i < 3; i++ {
    go func() {
        fmt.Println(i) // ❌ 所有goroutine共享同一变量i的地址
    }()
}
// 可能输出:3 3 3(非预期的0 1 2)

i 是循环变量,其内存地址在整个 for 作用域内不变;所有匿名函数捕获的是 &i,而非值拷贝。当 goroutine 实际执行时,循环早已结束,i 值为 3

根本原因分析

  • Go 中闭包捕获的是变量引用(指针语义),而非快照值;
  • for 循环不创建新作用域,i 仅声明一次;
  • goroutine 启动异步,执行时机不可控。

解决方案对比

方案 代码示意 是否安全 原理
参数传值 go func(val int) { ... }(i) 显式传入当前 i 的副本
变量遮蔽 for i := 0; i < 3; i++ { i := i; go func() { ... }() } 新声明同名局部变量,绑定当前值
graph TD
    A[for i := 0; i < 3; i++] --> B[启动 goroutine]
    B --> C{闭包捕获 &i}
    C --> D[所有 goroutine 共享 i 的内存地址]
    D --> E[最终读取到循环终止后的 i 值]

4.3 修复方案:显式参数传递 + sync.Pool 减少临时对象分配

核心问题定位

高频请求下,隐式闭包捕获导致 *bytes.Buffermap[string]interface{} 频繁堆分配,GC 压力陡增。

显式参数传递重构

func renderTemplate(ctx context.Context, data map[string]interface{}, buf *bytes.Buffer) error {
    buf.Reset() // 复用前清空
    // ... 模板渲染逻辑
    return nil
}

✅ 消除闭包隐式引用;❌ 不再依赖 goroutine 局部变量生命周期。buf 由调用方控制,语义清晰且可复用。

sync.Pool 集成策略

对象类型 Pool 获取方式 复用频次(QPS=5k)
*bytes.Buffer bufPool.Get().(*bytes.Buffer) ≈92%
map[string]any mapPool.Get().(map[string]any) ≈87%

对象生命周期管理

graph TD
    A[请求进入] --> B{从 sync.Pool 获取 buf/map}
    B --> C[显式传入 renderTemplate]
    C --> D[渲染完成,Reset/清空]
    D --> E[Put 回 Pool]

4.4 性能对比实验:泄漏版本 vs 修复版本的heap profile差异分析

我们使用 pprof 对比两个版本的堆内存快照:

# 采集修复版本 heap profile(30秒采样)
go tool pprof -http=":8081" http://localhost:6060/debug/pprof/heap?seconds=30

该命令触发持续30秒的堆分配采样,?seconds=30 确保捕获长生命周期对象,避免瞬时抖动干扰。

关键差异聚焦点

  • 泄漏版本中 *bytes.Buffer 实例数增长达 17×,且多数未被 GC 回收;
  • 修复版本引入 sync.Pool 复用缓冲区,runtime.mallocgc 调用频次下降 62%。

内存分布对比(Top 3 占比)

类型 泄漏版本 修复版本
*bytes.Buffer 78.3% 9.1%
[]byte 14.2% 53.7%
*http.Request 3.5% 2.8%

对象生命周期优化路径

graph TD
    A[原始请求] --> B[新建 bytes.Buffer]
    B --> C[写入响应体]
    C --> D[返回后 Buffer 逃逸至 goroutine]
    D --> E[GC 延迟回收]
    F[修复后] --> G[从 sync.Pool 获取 Buffer]
    G --> H[Use & Reset]
    H --> I[归还至 Pool]

第五章:从防御到观测:构建可持续演进的goroutine健康体系

在高并发微服务场景中,某支付网关曾因 goroutine 泄漏导致节点内存持续攀升,36 小时后 OOM Kill 触发服务雪崩。事后复盘发现,问题根源并非代码逻辑错误,而是缺乏对 goroutine 生命周期的可观测性——监控仅覆盖 CPU 与内存总量,却无法回答“哪些 goroutine 在阻塞?谁创建了它们?存活时间是否异常?”等关键问题。

基于 pprof 的实时 goroutine 快照分析

通过 HTTP /debug/pprof/goroutine?debug=2 接口可获取带栈帧的完整 goroutine 列表。生产环境我们封装了自动采样工具,在内存使用率突破 75% 时触发快照,并按 runtime.Stack() 输出过滤出阻塞型状态(如 semacquire, chan receive, select):

func dumpBlockingGoroutines() {
    buf := make([]byte, 2<<20)
    n := runtime.Stack(buf, true)
    re := regexp.MustCompile(`goroutine \d+ \[.*?(semacquire|chan receive|select).*?\]:`)
    matches := re.FindAll(buf[:n], -1)
    // 输出匹配栈帧并上报至日志平台
}

构建 goroutine 元数据追踪链路

我们在 context.WithValue 基础上扩展 goroutine.WithLabel,强制要求关键业务路径注入元标签:

标签键 示例值 采集方式
service payment-gateway 启动时全局注入
endpoint POST /v1/transfer HTTP 中间件自动提取
trace_id a1b2c3d4e5f67890 OpenTelemetry 透传
created_at 1717023489.213 time.Now().UnixNano()

该元数据随 goroutine 生命周期写入 ring buffer,并通过 eBPF 程序在内核态捕获 go_create 事件,实现跨用户态/内核态的关联。

可视化健康度看板与自愈策略

基于上述数据构建 Prometheus 指标:

  • go_goroutines_by_label{service="payment-gateway", endpoint="/v1/transfer"}
  • go_goroutine_age_seconds_bucket{le="300"}(直方图统计存活时长)

go_goroutines_by_label 连续 5 分钟增长斜率 > 120 goroutines/min 且 go_goroutine_age_seconds_bucket{le="300"} 占比 emergency_dump() 函数生成诊断包,包含 goroutine 栈、channel 状态、mutex 持有者信息。

案例:修复第三方 SDK 的 context.Done() 忽略漏洞

某 Redis 客户端 SDK 在超时后未检查 ctx.Done(),导致大量 goroutine 卡在 redisClient.Do(ctx, ...) 调用中。我们通过 pprof 快照定位到 237 个 goroutine 共享同一 ctx.WithTimeout(5s) 但未响应取消信号,随后向 SDK 提交 PR 并在本地 patch 中注入熔断钩子:

// patch: 强制检测 ctx.Done() 并提前退出
if err := ctx.Err(); err != nil {
    metrics.Inc("redis_timeout_ignored")
    return err
}

该补丁上线后,单节点 goroutine 峰值从 12,400 降至稳定 1,800,P99 响应延迟下降 42ms。

持续演进机制

我们建立 goroutine 健康基线模型:每日凌晨自动拉取前 7 天同时间段的 go_goroutines 指标,使用 STL 分解识别趋势项与季节项,当当前值偏离基线 3σ 时触发深度诊断流程。所有 goroutine 创建点均被 go:linkname 钩住,生成调用热点热力图,驱动代码重构优先级排序。

![goroutine health evolution flow](https://mermaid.ink/svg/pako:eNqFkU1rwzAMhv-K0NOhDZKlQwZjYxvDDmOHDiXJtVWwYyexk2H_3pOcLk3oYQ8S6dH76JGepEgQJkQqC5R0FZIuQqYKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAqgqkqkCqCqSqQKoKpKpAq

不张扬,只专注写好每一行 Go 代码。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注