#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""ots_anchor.py — OpenTimestamps（比特币锚定）for WDY records — 纯标准库。

把一条 WDY 记录的电子证据哈希（文件 SHA-256）提交至 OpenTimestamps 公开
日历网络，取得 .ots 证明文件；支持升级（待确认 → 比特币区块）与免节点核验
（公共区块浏览器多源交叉核对 Merkle 根）。

文件格式对齐 OpenTimestamps “Proof” v1（公开格式）：
  HEADER_MAGIC + varuint(major=1) + file-hash-op(0x08=SHA256) + digest
  + Timestamp 树（op 标记 f0 append / f1 prepend / f2 reverse / f3 hexlify /
    08 sha256；attestation 标记 00 + 8 字节类型 + varbytes 载荷；
    ff 前缀表示“后面还有兄弟条目”，ff00 = 追加 attestation）。
实现为独立重写（格式细节对照 python-opentimestamps 0.4.5 与公开规范），
仅使用标准库。

用法
----
  # 盖章（提交至默认日历网络；完成后写 .ots 并回写 manifest.anchors.opentimestamps）
  python ots_anchor.py --stamp 原件.pdf --record WDY-2026-0012-P2XK4P
  # 升级（出块后拿最终证明，并回写 manifest 状态）
  python ots_anchor.py --upgrade bitcoin.ots --record WDY-2026-0012-P2XK4P
  # 核验（文件摘要比对 + 比特币区块多源对照；无本地节点也可）
  python ots_anchor.py --verify bitcoin.ots -f 原件.pdf
  # 解析打印树
  python ots_anchor.py --info bitcoin.ots
  # 自检（序列化/解析往返 + DER/varint 向量）
  python ots_anchor.py --self-test

退出码：0 成功；1 失败；2 参数错误；3 全部日历提交失败。
"""

import argparse
import hashlib
import json
import sys
import urllib.request
from pathlib import Path

import wdy_anchor_common as C

# ─────────────────────────── OpenTimestamps 基础格式 ───────────────────────────
MAGIC = b"\x00OpenTimestamps\x00\x00Proof\x00\xbf\x89\xe2\xe8\x84\xe8\x92\x94"
TAG_ATTESTATION_PENDING = bytes.fromhex("83dfe30d2ef90c8e")
TAG_ATTESTATION_BITCOIN = bytes.fromhex("0588960d73d71901")

OP_SHA256 = 0x08
OP_APPEND = 0xF0
OP_PREPEND = 0xF1
OP_REVERSE = 0xF2
OP_HEXLIFY = 0xF3

DEFAULT_CALENDARS = [
    "https://a.pool.opentimestamps.org",
    "https://b.pool.opentimestamps.org",
    "https://a.pool.eternitywall.com",
    "https://ots.btc.catallaxy.com",
]


def w_varuint(v: int) -> bytes:
    out = bytearray()
    while True:
        b = v & 0x7F
        v >>= 7
        if v:
            out.append(b | 0x80)
        else:
            out.append(b)
            return bytes(out)


def r_varuint(buf, off):
    v = 0
    shift = 0
    while True:
        if off >= len(buf):
            raise ValueError("varuint: 数据截断")
        b = buf[off]
        off += 1
        v |= (b & 0x7F) << shift
        if not (b & 0x80):
            return v, off
        shift += 7


# ── 操作（Op）与时间戳树节点 ──────────────────────────────────────────────
def apply_op(key, msg: bytes) -> bytes:
    kind = key[0]
    if kind == "sha256":
        return hashlib.sha256(msg).digest()
    if kind == "append":
        return msg + key[1]
    if kind == "prepend":
        return key[1] + msg
    if kind == "reverse":
        return msg[::-1]
    if kind == "hexlify":
        return msg.hex().encode()
    raise ValueError(f"未知操作 {kind}")


def op_tag(key) -> bytes:
    kind = key[0]
    if kind == "sha256":
        return bytes([OP_SHA256])
    if kind == "append":
        return bytes([OP_APPEND])
    if kind == "prepend":
        return bytes([OP_PREPEND])
    if kind == "reverse":
        return bytes([OP_REVERSE])
    if kind == "hexlify":
        return bytes([OP_HEXLIFY])
    raise ValueError(f"未知操作 {kind}")


class Node:
    __slots__ = ("msg", "ops", "attestations")

    def __init__(self, msg):
        self.msg = msg
        self.ops = {}            # op_key -> Node
        self.attestations = []   # [("pending", uri) | ("bitcoin", height) | ("unknown", tag, payload)]


def op_sort_key(key):
    return (op_tag(key), key[1] if len(key) > 1 else b"")


def att_sort_key(att):
    if att[0] == "pending":
        return (TAG_ATTESTATION_PENDING, att[1])
    if att[0] == "bitcoin":
        return (TAG_ATTESTATION_BITCOIN, att[1])
    return (att[1], att[2].hex())


def parse_op(buf, off):
    tag = buf[off]
    off += 1
    if tag == OP_SHA256:
        return ("sha256",), off
    if tag in (OP_APPEND, OP_PREPEND):
        l, off = r_varuint(buf, off)
        data = buf[off:off + l]
        if len(data) != l:
            raise ValueError("op 参数截断")
        off += l
        return (("append" if tag == OP_APPEND else "prepend"), data), off
    if tag == OP_REVERSE:
        return ("reverse",), off
    if tag == OP_HEXLIFY:
        return ("hexlify",), off
    raise ValueError(f"不支持的操作标记 0x{tag:02x}")


def parse_attestation(buf, off):
    tag = buf[off:off + 8]
    if len(tag) != 8:
        raise ValueError("attestation tag 截断")
    off += 8
    l, off = r_varuint(buf, off)
    payload = buf[off:off + l]
    if len(payload) != l:
        raise ValueError("attestation 载荷截断")
    off += l
    if tag == TAG_ATTESTATION_PENDING:
        ul, o2 = r_varuint(payload, 0)
        uri = payload[o2:o2 + ul]
        return ("pending", uri.decode()), off
    if tag == TAG_ATTESTATION_BITCOIN:
        height, _ = r_varuint(payload, 0)
        return ("bitcoin", height), off
    return ("unknown", tag, payload), off


def read_node(buf, off, node):
    """按格式读取一个节点（递归下降；自描述、无长度前缀，规则见模块 docstring）。"""
    while True:
        if off >= len(buf):
            raise ValueError("timestamp 树截断（节点未闭合）")
        b = buf[off]
        off += 1
        if b == 0xFF:
            if off >= len(buf):
                raise ValueError("ff 标记后截断")
            b2 = buf[off]
            off += 1
            if b2 == 0x00:
                att, off = parse_attestation(buf, off)
                node.attestations.append(att)
                continue
            key, off = parse_op_tagged(b2, buf, off)
            child = Node(apply_op(key, node.msg))
            child, off = read_node(buf, off, child)
            _attach(node, key, child)
            continue
        if b == 0x00:
            att, off = parse_attestation(buf, off)
            node.attestations.append(att)
            return node, off
        key, off = parse_op_tagged(b, buf, off)
        child = Node(apply_op(key, node.msg))
        child, off = read_node(buf, off, child)
        _attach(node, key, child)
        return node, off


def parse_op_tagged(tag_byte, buf, off):
    """读取“已知标记 + 参数”的操作（tag_byte 已消费）。"""
    if tag_byte == OP_SHA256:
        return ("sha256",), off
    if tag_byte in (OP_APPEND, OP_PREPEND):
        l, off = r_varuint(buf, off)
        data = buf[off:off + l]
        if len(data) != l:
            raise ValueError("op 参数截断")
        off += l
        return (("append" if tag_byte == OP_APPEND else "prepend"), data), off
    if tag_byte == OP_REVERSE:
        return ("reverse",), off
    if tag_byte == OP_HEXLIFY:
        return ("hexlify",), off
    raise ValueError(f"不支持的操作标记 0x{tag_byte:02x}")


def _attach(node, key, child):
    if key in node.ops:
        merge_nodes(node.ops[key], child)
    else:
        node.ops[key] = child


def merge_nodes(dst: Node, src: Node):
    if dst.msg != src.msg:
        raise ValueError("合并了不同消息的节点")
    for att in src.attestations:
        if att not in dst.attestations:
            dst.attestations.append(att)
    for key, child in src.ops.items():
        if key in dst.ops:
            merge_nodes(dst.ops[key], child)
        else:
            dst.ops[key] = child


def serialize_node(node: Node) -> bytes:
    attestations = sorted(node.attestations, key=att_sort_key)
    ops = sorted(node.ops.items(), key=lambda kv: op_sort_key(kv[0]))
    out = bytearray()
    if len(attestations) > 1:
        for att in attestations[:-1]:
            out += b"\xff\x00" + serialize_attestation(att)
    if not ops:
        out += b"\x00" + serialize_attestation(attestations[-1])
        return bytes(out)
    if attestations:
        out += b"\xff\x00" + serialize_attestation(attestations[-1])
    for key, child in ops[:-1]:
        out += b"\xff" + op_tag(key)
        if key[0] in ("append", "prepend"):
            out += w_varuint(len(key[1])) + key[1]
        out += serialize_node(child)
    key, child = ops[-1]
    out += op_tag(key)
    if key[0] in ("append", "prepend"):
        out += w_varuint(len(key[1])) + key[1]
    out += serialize_node(child)
    return bytes(out)


def serialize_attestation(att) -> bytes:
    if att[0] == "pending":
        payload = w_varuint(len(att[1].encode())) + att[1].encode()
        return TAG_ATTESTATION_PENDING + w_varuint(len(payload)) + payload
    if att[0] == "bitcoin":
        payload = w_varuint(att[1])
        return TAG_ATTESTATION_BITCOIN + w_varuint(len(payload)) + payload
    return att[1] + w_varuint(len(att[2])) + att[2]


def serialize_detached(digest: bytes, node: Node) -> bytes:
    return MAGIC + w_varuint(1) + bytes([OP_SHA256]) + digest + serialize_node(node)


def parse_detached(buf: bytes):
    if not buf.startswith(MAGIC):
        raise ValueError("不是 OpenTimestamps .ots 文件（magic 不符）")
    off = len(MAGIC)
    ver, off = r_varuint(buf, off)
    if ver != 1:
        raise ValueError(f"不支持的 .ots 主版本 {ver}")
    if buf[off] != OP_SHA256:
        raise ValueError(f"file-hash op 非 SHA256（0x{buf[off]:02x}）")
    off += 1
    digest = buf[off:off + 32]
    if len(digest) != 32:
        raise ValueError("digest 截断")
    off += 32
    node, off = read_node(buf, off, Node(digest))
    if off != len(buf):
        raise ValueError("文件尾部有多余数据（trailing garbage）")
    return digest, node


# ─────────────────────────── 日历通信 ───────────────────────────
def _request(url, data=None, timeout=45):
    req = urllib.request.Request(
        url, data=data,
        headers={"Accept": "application/vnd.opentimestamps.v1",
                 "User-Agent": "wdy-ots-anchor/1.0"})
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        if resp.status != 200:
            raise RuntimeError(f"HTTP {resp.status}")
        return resp.read(65536)


def calendar_submit(base_url, digest, timeout=45):
    """POST {base}/digest，返回解析后的 Timestamp 节点（initial msg=digest）。"""
    data = _request(base_url.rstrip("/") + "/digest", data=digest, timeout=timeout)
    node, off = read_node(data, 0, Node(digest))
    if off != len(data):
        raise ValueError("日历响应尾部多余数据")
    return node


def calendar_get(base_url, commitment_hex, timeout=45):
    """GET {base}/timestamp/{hex}，未收录时返回 None。"""
    url = base_url.rstrip("/") + "/timestamp/" + commitment_hex
    try:
        data = _request(url, timeout=timeout)
    except urllib.error.HTTPError as e:
        if e.code == 404:
            return None
        raise
    node, off = read_node(data, 0, Node(bytes.fromhex(commitment_hex)))
    if off != len(data):
        raise ValueError("日历响应尾部多余数据")
    return node


import urllib.error  # noqa: E402  (上面 except 用)


def iter_nodes(node: Node, path=()):
    yield node, path
    for key, child in node.ops.items():
        yield from iter_nodes(child, path + (key,))


def find_pending(node: Node):
    """返回 [(节点, 路径)]，节点上带 pending attestations。"""
    out = []
    for n, p in iter_nodes(node):
        for att in n.attestations:
            if att[0] == "pending":
                out.append((n, p, att[1]))
    return out


def find_bitcoin(node: Node):
    out = []
    for n, p in iter_nodes(node):
        for att in n.attestations:
            if att[0] == "bitcoin":
                out.append((n, p, att[1]))
    return out


# ─────────────────────────── 命令实现 ───────────────────────────
def cmd_stamp(path, record=None, calendars=None, out=None, manifest=None, key=None,
              publish=True, timeout=45):
    src = Path(path)
    digest = hashlib.sha256(src.read_bytes()).digest()
    calendars = calendars or DEFAULT_CALENDARS
    root = Node(digest)
    ok_cals, errs = [], []
    for url in calendars:
        try:
            sub = calendar_submit(url, digest, timeout=timeout)
            merge_nodes(root, sub)
            ok_cals.append(url)
            print(f"  ✓ {url}")
        except Exception as e:
            errs.append(f"{url}: {e}")
            print(f"  ✗ {url}: {e}")
    if not ok_cals:
        print("\n全部日历提交失败，未产生证明。")
        return 3
    raw = serialize_detached(digest, root)
    rid = record
    if out is None and rid:
        out = C.INTL / rid / "bitcoin.ots"
    out = Path(out or (C.INTL / (src.stem + ".ots")))
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_bytes(raw)
    print(f"\n.ots 已写入：{out}（{len(raw)} 字节，sha256={hashlib.sha256(raw).hexdigest()[:16]}…）")
    info = {
        "digest": digest.hex(), "calendars": ok_cals, "errors": errs,
        "ots_file": str(out), "ots_sha256": hashlib.sha256(raw).hexdigest(),
        "pending": len(find_pending(root)), "bitcoin": len(find_bitcoin(root)),
    }
    if rid and manifest and key:
        anchor = {
            "type": "opentimestamps", "protocol": "OpenTimestamps v1",
            "chain": "Bitcoin", "digest_algorithm": "sha256",
            "message_digest": digest.hex(), "calendars": ok_cals,
            "proof_file": out.name, "proof_sha256": info["ots_sha256"],
            "status": "pending", "block_height": None,
            "submitted_at": C.now_iso(), "confirmed_at": None,
        }
        from wdy_anchor_common import merge_manifest_anchor, publish_to_verify
        m, _ = merge_manifest_anchor(manifest, key, "opentimestamps", anchor)
        print(f"manifest 已回写 anchors.opentimestamps 并重签（{Path(manifest).name}）")
        pub = publish_to_verify(rid, {out: "bitcoin.ots",
                                      Path(manifest): "manifest.json"})
        if pub:
            print("已发布到验证页目录：", ", ".join(pub))
        info["manifest"] = str(manifest)
        info["published"] = pub
    C.append_report({"tool": "ots_anchor", "action": "stamp", "record": rid, **info})
    return 0


def cmd_upgrade(path, record=None, manifest=None, key=None, publish=True, timeout=45):
    p = Path(path)
    digest, root = parse_detached(p.read_bytes())
    changed = False
    for _ in range(6):
        pending = find_pending(root)
        if not pending:
            break
        any_new = False
        for node, _, uri in pending:
            if not uri.startswith("http"):
                continue
            try:
                got = calendar_get(uri, node.msg.hex(), timeout=timeout)
            except Exception as e:
                print(f"  ! {uri}: {e}")
                continue
            if got is None:
                continue
            merge_nodes(node, got)
            any_new = True
        if not any_new:
            break
        changed = True
    btc = find_bitcoin(root)
    if changed:
        raw = serialize_detached(digest, root)
        p.write_bytes(raw)
        print(f"已升级并写回：{p}（{len(raw)} 字节）")
    else:
        print("暂无新证明材料（日历仍待比特币确认，稍后再试）")
    if btc:
        heights = sorted({h for _, _, h in btc})
        print(f"✅ 已锚定至比特币区块高度：{heights}")
    else:
        print("⏳ 状态：待比特币确认（一般数小时内完成）")
    if record and manifest and key and btc:
        from wdy_anchor_common import merge_manifest_anchor, publish_to_verify
        heights = sorted({h for _, _, h in btc})
        m = json.loads(Path(manifest).read_text(encoding="utf-8"))
        anchor = (m.get("anchors") or {}).get("opentimestamps") or {}
        anchor.update({"status": "confirmed", "block_height": heights[-1],
                       "confirmed_at": C.now_iso(),
                       "proof_sha256": hashlib.sha256(p.read_bytes()).hexdigest()})
        merge_manifest_anchor(manifest, key, "opentimestamps", anchor)
        print("manifest 已更新 anchors.opentimestamps 状态并重签")
        pub = publish_to_verify(record, {p: "bitcoin.ots", Path(manifest): "manifest.json"})
        if pub:
            print("已发布到验证页目录：", ", ".join(pub))
    C.append_report({"tool": "ots_anchor", "action": "upgrade", "record": record,
                     "file": str(p), "bitcoin_heights": [h for _, _, h in btc]})
    return 0


def fetch_block(api, height, timeout=30):
    """公共区块浏览器：返回 (block_hash, header_json)。"""
    base, jn = api
    with urllib.request.urlopen(f"{base}/block-height/{height}", timeout=timeout) as r:
        block_hash = r.read().decode().strip()
    with urllib.request.urlopen(f"{base}/block/{block_hash}", timeout=timeout) as r:
        j = json.loads(r.read().decode())
    return block_hash, j


def cmd_verify(path, file=None, timeout=30):
    p = Path(path)
    digest, root = parse_detached(p.read_bytes())
    print(f".ots 文件：{p}")
    print(f"记录摘要（sha256）：{digest.hex()}")
    ok = True
    if file:
        h = hashlib.sha256(Path(file).read_bytes()).hexdigest()
        match = (h == digest.hex())
        ok &= match
        print(f"原件摘要比对：{'一致 ✓' if match else '不一致 ✗'}（{h}）")
    btc = find_bitcoin(root)
    pending = find_pending(root)
    if not btc:
        print(f"状态：⏳ 待比特币确认（pending 日历条目 {len(pending)} 个）")
        print("提示：一般数小时内确认；确认后运行 --upgrade 并重新核验。")
        C.append_report({"tool": "ots_anchor", "action": "verify", "file": str(p),
                         "result": "pending", "pending": len(pending)})
        return 0 if ok else 1
    apis = [("https://mempool.space/api", "mempool.space"),
            ("https://blockstream.info/api", "blockstream.info")]
    results = []
    for node, _, h in sorted(btc, key=lambda x: x[2]):
        root_hex = node.msg.hex()
        print(f"\n比特币区块高度 {h}：期望 Merkle 根 {root_hex[:24]}…")
        for base, name in apis:
            try:
                bh, j = fetch_block((base, name), h, timeout=timeout)
                jr = j.get("merkle_root")
                t = j.get("timestamp")
                good = (jr == root_hex)
                ok &= good
                print(f"  [{name}] 区块 {bh[:16]}… merkle={'一致 ✓' if good else '不一致 ✗'}"
                      f" 时间={t}")
                results.append({"source": name, "height": h, "block": bh,
                                "merkle_root_match": good, "time": t})
            except Exception as e:
                print(f"  [{name}] 查询失败：{e}")
                ok = False
    C.append_report({"tool": "ots_anchor", "action": "verify", "file": str(p),
                     "result": "confirmed" if ok else "mismatch", "checks": results})
    print("\n结论：", "✅ 摘要与比特币区块 Merkle 根一致（多源核验）" if ok else "⚠ 存在未通过项")
    return 0 if ok else 1


def cmd_info(path):
    digest, root = parse_detached(Path(path).read_bytes())
    print(f"digest: {digest.hex()}")
    for n, path_ in iter_nodes(root):
        if n.attestations or not n.ops:
            label = " → ".join(_op_label(k) for k in path_) or "(根)"
            atts = ", ".join(_att_label(a) for a in n.attestations) or "-"
            print(f"  {label}\n      msg={n.msg.hex()[:32]}…\n      attestation: {atts}")
    return 0


def _op_label(key):
    if key[0] in ("append", "prepend"):
        return f"{key[0]}({key[1].hex()[:16]}…)" if len(key[1]) > 8 else f"{key[0]}({key[1].hex()})"
    return key[0]


def _att_label(a):
    if a[0] == "pending":
        return f"pending({a[1]})"
    if a[0] == "bitcoin":
        return f"bitcoin(height={a[1]})"
    return f"unknown({a[1].hex()})"


def cmd_self_test():
    ok = True
    v = w_varuint(300)
    r, off = r_varuint(v, 0)
    ok &= (r == 300 and off == len(v))
    print(f"  varuint 往返          {'PASS' if r == 300 else 'FAIL'}")

    digest = hashlib.sha256(b"wdy").digest()
    node = Node(digest)
    n2 = Node(apply_op(("append", b"\x01\x02"), digest))
    n2.attestations.append(("pending", "https://a.pool.opentimestamps.org"))
    n3 = Node(apply_op(("prepend", b"\x03"), digest))
    n3.attestations.append(("bitcoin", 800000))
    node.ops[("append", b"\x01\x02")] = n2
    node.ops[("prepend", b"\x03")] = n3
    raw = serialize_detached(digest, node)
    d2, root2 = parse_detached(raw)
    raw2 = serialize_detached(d2, root2)
    ok &= (d2 == digest and raw2 == raw)
    print(f"  树序列化/解析往返      {'PASS' if raw2 == raw else 'FAIL'}")
    raw3 = serialize_detached(digest, node)
    ok &= (raw3 == raw)
    print(f"  重复序列化确定性       {'PASS' if raw3 == raw else 'FAIL'}")

    multi = Node(digest)
    multi.attestations.append(("pending", "https://b.pool.opentimestamps.org"))
    multi.attestations.append(("pending", "https://a.pool.opentimestamps.org"))
    raw4 = serialize_detached(digest, multi)
    d4, root4 = parse_detached(raw4)
    ok &= (serialize_detached(d4, root4) == raw4 and len(root4.attestations) == 2)
    print(f"  多 attestation 往返    {'PASS' if serialize_detached(d4, root4) == raw4 else 'FAIL'}")
    print("自检", "4/4 PASS" if ok else "存在 FAIL")
    return 0 if ok else 1


def main():
    ap = argparse.ArgumentParser(description="OpenTimestamps 锚定（WDY）")
    ap.add_argument("--stamp", metavar="FILE", default=None, help="盖章：提交文件哈希至日历网络")
    ap.add_argument("--upgrade", metavar="FILE.ots", default=None, help="升级：拉取最终证明")
    ap.add_argument("--verify", metavar="FILE.ots", default=None, help="核验：摘要比对 + 比特币区块多源核对")
    ap.add_argument("--info", metavar="FILE.ots", default=None, help="解析并打印 .ots")
    ap.add_argument("--self-test", action="store_true")
    ap.add_argument("-f", "--file", default=None, help="--verify 用的原件（比对 SHA-256）")
    ap.add_argument("--record", default=None, help="记录号（回写 manifest 与发布文件）")
    ap.add_argument("--manifest", default=None, help="manifest 路径（默认按记录号自动定位）")
    ap.add_argument("--key", default=None, help="Ed25519 私钥（默认 work/wdy-2026-rot1.key）")
    ap.add_argument("--out", default=None, help=".ots 输出路径")
    ap.add_argument("--calendars", default=None, help="逗号分隔的日历 URL（默认官方四家）")
    ap.add_argument("--timeout", type=int, default=45)
    a = ap.parse_args()

    def _autopaths():
        manifest, key = a.manifest, a.key
        if a.record and not manifest:
            p = C.WORK / f"{a.record}.manifest.json"
            if p.exists():
                manifest = str(p)
        if not key:
            p = C.WORK / "wdy-2026-rot1.key"
            if p.exists():
                key = str(p)
        return manifest, key

    if a.self_test:
        sys.exit(cmd_self_test())
    if a.stamp:
        cals = [x.strip() for x in a.calendars.split(",")] if a.calendars else None
        manifest, key = _autopaths()
        sys.exit(cmd_stamp(a.stamp, record=a.record, calendars=cals, out=a.out,
                           manifest=manifest, key=key, timeout=a.timeout))
    if a.upgrade:
        manifest, key = _autopaths()
        sys.exit(cmd_upgrade(a.upgrade, record=a.record, manifest=manifest, key=key,
                             timeout=a.timeout))
    if a.verify:
        sys.exit(cmd_verify(a.verify, file=a.file, timeout=a.timeout))
    if a.info:
        sys.exit(cmd_info(a.info))
    ap.print_help()


if __name__ == "__main__":
    main()
