feat(791-1): 엔진 셈 의미 넓힘 — 금액번지 · 소계 단계 · 제외 · 줄 자리 · 블록 끝전 · 결과 조정 · 변수 표시
- 새 모듈 master_formula_sum.py — 덧줄 고리(master_formula.run 에서 옮김) · 번지 · 단계 · 합밖 · 조정 · 표시 · 검사
- master_cut.py — 줄 자리 {방법, 자리} · 블록 끝전(비목버림 | 큰나머지) · 결과 조정 · 답 단계 목록
- master_formula_check.py — 새 칸 검사(없는 번지 · 단계 순서 · 자리 범위 · 끝전 · 조정)
- _틀.md 7장 「셈 의미 넓힘」 · 8장 덧줄 이름
- 지킴 시험 test_791_1 — M01 로직 1,515 지문 전후 같음 · 새 의미 단위 · 실무 견본 넷(봉화 D00102 · 울진소광 D01489 · 동부청영월 D00003 · 북부청임도 18) = 엑셀
- master_formula.py 690 → 669 줄
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ from decimal import Decimal
|
||||
from pathlib import Path
|
||||
|
||||
import master_formula as mf
|
||||
import master_formula_sum as ms
|
||||
|
||||
_ROOT = str(Path(__file__).resolve().parents[3])
|
||||
if _ROOT not in sys.path: # 스크립트 · 시험 길 — 서버는 이미 뿌리에서 돎
|
||||
@@ -39,9 +40,19 @@ def down(value, places: int):
|
||||
return Decimal(str(rounddown(float(value), places)))
|
||||
|
||||
|
||||
def _parts(split: dict) -> tuple[dict, Decimal | None]:
|
||||
"""줄 비목 → (비목마다 0.1 원 버림, 금액 = 그 합 · 모두 빈 값 = None)."""
|
||||
cut = {k: down(v, 1) for k, v in split.items()}
|
||||
def _line(value, rule: dict | None):
|
||||
"""줄 값 끝수 — 줄 `자리` {방법, 자리}(791-1) · 없으면 0.1 원 버림."""
|
||||
if value is None or not rule:
|
||||
return down(value, 1)
|
||||
way, places = rule.get("방법"), int(rule.get("자리") or 0)
|
||||
if way == "버림":
|
||||
return down(value, places)
|
||||
return mf._round(Decimal(str(value)), places, mf.ROUND_WAYS[way])
|
||||
|
||||
|
||||
def _parts(split: dict, rule: dict | None = None) -> tuple[dict, Decimal | None]:
|
||||
"""줄 비목 → (비목마다 0.1 원 버림 · 줄 `자리` 면 그것, 금액 = 그 합 · 모두 빈 = None)."""
|
||||
cut = {k: _line(v, rule) for k, v in split.items()}
|
||||
kept = [v for v in cut.values() if v is not None]
|
||||
return cut, (sum(kept, Decimal(0)) if kept else None)
|
||||
|
||||
@@ -84,6 +95,9 @@ def engine(master, ref: str, got: dict, depth: int = 0) -> dict:
|
||||
if isinstance(logic.get("흐름"), dict):
|
||||
return got
|
||||
items, adds = logic.get("호표") or [], logic.get("덧줄") or []
|
||||
rules = [x.get("자리") for x in (*items, *adds)] # 줄 자리(791-1)
|
||||
new = ms.uses(logic)
|
||||
skip = ms.skipped(logic) if new else set()
|
||||
env = dict(got.get("중간") or {})
|
||||
total = dict.fromkeys(COSTS, Decimal(0))
|
||||
lines = []
|
||||
@@ -100,21 +114,40 @@ def engine(master, ref: str, got: dict, depth: int = 0) -> dict:
|
||||
elif n - len(items) < len(adds) and line.get("금액") is not None:
|
||||
add = adds[n - len(items)]
|
||||
env.update(total)
|
||||
if new: # 덧줄이 부르는 번지 · 단계 = 버린 줄로
|
||||
env.update(ms.scope(logic, lines))
|
||||
try:
|
||||
value = mf.evaluate(mf.parse(add["식"]), env, master, depth)
|
||||
except _ERRORS:
|
||||
value = line["금액"]
|
||||
env[add["이름"]] = down(value, 1) # 뒤 덧줄 · 중간 칸 = 버린 값
|
||||
env[add["이름"]] = _line(value, rules[n]) # 뒤 덧줄 · 중간 칸 = 버린 값
|
||||
split = {add["비목"]: value}
|
||||
cut, money = _parts(split)
|
||||
cut, money = _parts(split, rules[n] if n < len(rules) else None)
|
||||
for k, v in cut.items():
|
||||
total[k] += v or 0
|
||||
total[k] += 0 if n in skip else v or 0
|
||||
if n >= len(items) and money is not None:
|
||||
more = {"단가": money}
|
||||
lines.append({**line, "비목": cut, "금액": money, **more})
|
||||
costs = {k: mf.cut(logic, down(v, 0), "계") for k, v in total.items()}
|
||||
# 중간 = 덧줄이 본 값(버린 줄 합 · 덧줄 값 · `줄`) — 시험 계산 중간 칸
|
||||
return {**got, "줄": lines, **costs, "계": sum(costs.values()), "중간": env}
|
||||
if not new:
|
||||
costs = {k: mf.cut(logic, down(v, 0), "계") for k, v in total.items()}
|
||||
# 중간 = 덧줄이 본 값(버린 줄 합 · 덧줄 값 · `줄`) — 시험 계산 중간 칸
|
||||
return {**got, "줄": lines, **costs, "계": sum(costs.values()), "중간": env}
|
||||
costs = settle(logic, total)
|
||||
more = {"단계": ms.steps(logic, lines)}
|
||||
return {**got, "줄": lines, **costs, "계": sum(costs.values()), "중간": env, **more}
|
||||
|
||||
|
||||
def settle(logic: dict, total: dict) -> dict:
|
||||
"""블록 끝전(791-1) — `끝전` 비목버림(비목마다 원 버림 · 옛 규칙) · 큰나머지(계 원 버림 뒤
|
||||
모자란 원을 소수 큰 비목부터 · 같으면 경 › 재 › 노) → 로직 `끝수` 묶음 → `조정` → 원 버림."""
|
||||
costs = {k: down(v, 0) for k, v in total.items()}
|
||||
if logic.get("끝전") == "큰나머지":
|
||||
rest = int(down(sum(total.values()), 0) - sum(costs.values()))
|
||||
order = sorted(COSTS, key=lambda k: (costs[k] - total[k], COSTS[::-1].index(k)))
|
||||
for k in order[:rest]:
|
||||
costs[k] += 1
|
||||
costs = ms.adjust(logic, {k: mf.cut(logic, v, "계") for k, v in costs.items()})
|
||||
return {k: down(v, 0) for k, v in costs.items()}
|
||||
|
||||
|
||||
def plain(got: dict) -> dict:
|
||||
|
||||
Reference in New Issue
Block a user