import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from scipy.interpolate import UnivariateSpline
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
class SulfosalicylateIronExperiment:
def __init__(self):
self.fe_concentration = 0.001
self.ligand_concentration = 0.001
self.hclo4_concentration = 0.01
self.hclo4_volume = 10.0
self.total_mixing_volume = 10.0
self.total_volume = self.hclo4_volume + self.total_mixing_volume
self.mole_fractions = np.array([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])
self.absorbances = None
self.absorbances_all = None
def input_data(self):
print("=== 磺基水杨酸铁配位数和稳定常数测定实验数据处理 ===")
print("\n请输入11个数据点的吸光度值 (平行实验):")
print("格式: 点1实验1 点1实验2 点2实验1 点2实验2 ... 点11实验1 点11实验2")
data_str = input("请输入22个吸光度值: ")
data = list(map(float, data_str.split()))
if len(data) != 22:
raise ValueError
("必须输入22个吸光度值!")
self.absorbances_all = np.array(data).reshape(11, 2)
self.absorbances = np.mean(self.absorbances_all, axis=1)
self.absorbances_std = np.std(self.absorbances_all, axis=1, ddof=1)
print("\n数据输入完成!")
print(f"摩尔分数: {self.mole_fractions}")
print(f"平均吸光度: {self.absorbances}")
return self.mole_fractions, self.absorbances
def linear_fit(self, x_data, y_data):
A = np.vstack([x_data, np.ones(len(x_data))]).T
m, c = np.linalg.lstsq(A, y_data, rcond=None)[0]
return m, c
def calculate_intersection(self, m1, c1, m2, c2):
x_intersect = (c2 - c1) / (m1 - m2)
y_intersect = m1 * x_intersect + c1
return x_intersect, y_intersect
def find_curve_intersection(self, x_intersect):
for i in range(len(self.mole_fractions)-1):
if self.mole_fractions[i] <= x_intersect <= self.mole_fractions[i+1]:
x1, x2 = self.mole_fractions[i], self.mole_fractions[i+1]
y1, y2 = self.absorbances[i], self.absorbances[i+1]
y_curve = y1 + (y2 - y1) * (x_intersect - x1) / (x2 - x1)
return y_curve
return self.absorbances[np.argmax(self.absorbances)]
def calculate_acid_effect(self, pH=2.5):
H_plus = 10**(-pH)
K2 = 10**(-2.6)
K3 = 10**(-11.7)
alpha = 1 + H_plus/K3 + H_plus**2/(K2*K3)
lg_alpha
= np.
log10(alpha
)
return lg_alpha
def analyze_data(self):
if self.absorbances is None:
raise ValueError
("请先输入数据!")
left_indices = [0, 1, 2]
right_indices = [8, 9, 10]
m_left, c_left = self.linear_fit(
self.mole_fractions[left_indices],
self.absorbances[left_indices]
)
m_right, c_right = self.linear_fit(
self.mole_fractions[right_indices],
self.absorbances[right_indices]
)
x_intersect, D1 = self.calculate_intersection(m_left, c_left, m_right, c_right)
D2 = self.find_curve_intersection(x_intersect)
coordination_number = x_intersect / (1 - x_intersect)
if D1 > 0:
dissociation_degree = (D1 - D2) / D1
else:
dissociation_degree = 0
fe_initial_moles = self.fe_concentration * (1 - x_intersect) * (self.total_mixing_volume / 2) / 1000
complex_concentration = fe_initial_moles / (self.total_volume / 1000)
if dissociation_degree > 0 and dissociation_degree < 1:
apparent_constant = (1 - dissociation_degree) / (complex_concentration * dissociation_degree**2)
lg_alpha = self.calculate_acid_effect()
stability_constant = apparent_constant * (10**lg_alpha)
else:
stability_constant = float('inf')
apparent_constant = 0
results = {
'D1': D1,
'D2': D2,
'x_intersect': x_intersect,
'coordination_number': coordination_number,
'dissociation_degree': dissociation_degree,
'stability_constant': stability_constant,
'apparent_constant': apparent_constant,
'lg_alpha': lg_alpha,
'left_fit': (m_left, c_left),
'right_fit': (m_right, c_right),
'complex_concentration': complex_concentration
}
return results
def plot_results(self, results):
if self.absorbances is None:
raise ValueError
("请先输入数据!")
plt.figure(figsize=(12, 8))
confidence_sizes = 100 + 200 * (1 / (self.absorbances_std + 0.001) / np.max(1 / (self.absorbances_std + 0.001)))
plt.scatter(self.mole_fractions, self.absorbances, s=confidence_sizes, alpha=0.7, color='blue', label='实验数据点')
for i, (x, y) in enumerate(zip(self.mole_fractions, self.absorbances)):
plt.annotate(f'{y:.3f}', (x, y), textcoords="offset points", xytext=(0,10), ha='center', fontsize=9)
if len(self.mole_fractions) > 3:
spline = UnivariateSpline(self.mole_fractions, self.absorbances, s=0.001)
x_smooth = np.linspace(0, 1, 200)
y_smooth = spline(x_smooth)
plt.plot(x_smooth, y_smooth, 'b-', alpha=0.7, linewidth=2, label='实验曲线')
x_left = np.array([0.0, results['x_intersect']])
x_right = np.array([results['x_intersect'], 1.0])
m_left, c_left = results['left_fit']
m_right, c_right = results['right_fit']
x_fit_left = np.linspace(0.0, results['x_intersect'], 50)
y_fit_left = m_left * x_fit_left + c_left
x_fit_right = np.linspace(results['x_intersect'], 1.0, 50)
y_fit_right = m_right * x_fit_right + c_right
plt.plot(x_fit_left, y_fit_left, 'r--', linewidth=2, label='左侧拟合直线')
plt.plot(x_fit_right, y_fit_right, 'g--', linewidth=2, label='右侧拟合直线')
plt.text(0.1, 0.1, f'左侧: y = {m_left:.3f}x + {c_left:.3f}', transform=plt.gca().transAxes, fontsize=10, color='red')
plt.text(0.1, 0.05, f'右侧: y = {m_right:.3f}x + {c_right:.3f}', transform=plt.gca().transAxes, fontsize=10, color='green')
plt.axvline(x=results['x_intersect'], color='gray', linestyle=':', alpha=0.7, label=f'交点X坐标: {results["x_intersect"]:.3f}')
plt.scatter([results['x_intersect']], [results['D1']], color='red', s=100, label='理论交点')
plt.scatter([results['x_intersect']], [results['D2']], color='green', s=100, label='实际交点')
plt.annotate(f'理论: {results["D1"]:.3f}',
(results['x_intersect'], results['D1']),
textcoords="offset points", xytext=(10,10), ha='left', fontsize=9, color='red')
plt.annotate(f'实际: {results["D2"]:.3f}',
(results['x_intersect'], results['D2']),
textcoords="offset points", xytext=(10,-15), ha='left', fontsize=9, color='green')
plt.xlabel('磺基水杨酸的摩尔分数')
plt.ylabel('吸光度 (A)')
plt.title('磺基水杨酸铁(Ⅲ)配合物组成与稳定常数测定结果图', fontsize=20, pad=20)
plt.text(0.98, 0.98, "由何同学与D老师联合制作",
transform=plt.gca().transAxes,
fontsize=6, color='gray',
ha='right', va='top',
bbox=dict(boxstyle="round,pad=0.2", facecolor="white", alpha=0.8))
textstr = '\n'.join([
f'配合物组成: ML{results["coordination_number"]:.1f}',
f'交点X坐标: {results["x_intersect"]:.3f}',
f'解离度: {results["dissociation_degree"]:.4f}',
f'稳定常数: {results["stability_constant"]:.2e} L/mol'
])
props = dict(boxstyle='round', facecolor='wheat', alpha=0.8)
plt.text(0.65, 0.25, textstr, transform=plt.gca().transAxes, fontsize=12,
verticalalignment='top', bbox=props)
plt.grid(True, alpha=0.3)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()
return plt.gcf()
def run_analysis(self):
try:
self.input_data()
results = self.analyze_data()
print("\n" + "="*60)
print("实验结果分析")
print("="*60)
print(f"配合物组成: ML{results['coordination_number']:.1f}")
print(f"交点X坐标: {results['x_intersect']:.3f}")
print(f"解离度: {results['dissociation_degree']:.4f}")
print(f"表观稳定常数: {results['apparent_constant']:.2e} L/mol")
print(f"酸效应系数 lgα: {results['lg_alpha']:.3f}")
print(f"热力学稳定常数: {results['stability_constant']:.2e} L/mol")
print("\n生成图表中...")
self.plot_results(results)
print("图表已显示,请查看弹出窗口。")
except Exception as e:
print(f"错误: {e}")
print("请检查输入数据格式是否正确。")
if __name__ == "__main__":
experiment = SulfosalicylateIronExperiment()
experiment.run_analysis()
aW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKZnJvbSBzY2lweSBpbXBvcnQgc3RhdHMKZnJvbSBzY2lweS5pbnRlcnBvbGF0ZSBpbXBvcnQgVW5pdmFyaWF0ZVNwbGluZQppbXBvcnQgd2FybmluZ3MKd2FybmluZ3MuZmlsdGVyd2FybmluZ3MoJ2lnbm9yZScpCgpwbHQucmNQYXJhbXNbJ2ZvbnQuc2Fucy1zZXJpZiddID0gWydTaW1IZWknLCAnTWljcm9zb2Z0IFlhSGVpJywgJ0RlamFWdSBTYW5zJ10KcGx0LnJjUGFyYW1zWydheGVzLnVuaWNvZGVfbWludXMnXSA9IEZhbHNlCgpjbGFzcyBTdWxmb3NhbGljeWxhdGVJcm9uRXhwZXJpbWVudDoKICAgIGRlZiBfX2luaXRfXyhzZWxmKToKICAgICAgICBzZWxmLmZlX2NvbmNlbnRyYXRpb24gPSAwLjAwMQogICAgICAgIHNlbGYubGlnYW5kX2NvbmNlbnRyYXRpb24gPSAwLjAwMQogICAgICAgIHNlbGYuaGNsbzRfY29uY2VudHJhdGlvbiA9IDAuMDEKICAgICAgICBzZWxmLmhjbG80X3ZvbHVtZSA9IDEwLjAKICAgICAgICBzZWxmLnRvdGFsX21peGluZ192b2x1bWUgPSAxMC4wCiAgICAgICAgc2VsZi50b3RhbF92b2x1bWUgPSBzZWxmLmhjbG80X3ZvbHVtZSArIHNlbGYudG90YWxfbWl4aW5nX3ZvbHVtZQogICAgICAgIHNlbGYubW9sZV9mcmFjdGlvbnMgPSBucC5hcnJheShbMC4wLCAwLjEsIDAuMiwgMC4zLCAwLjQsIDAuNSwgMC42LCAwLjcsIDAuOCwgMC45LCAxLjBdKQogICAgICAgIHNlbGYuYWJzb3JiYW5jZXMgPSBOb25lCiAgICAgICAgc2VsZi5hYnNvcmJhbmNlc19hbGwgPSBOb25lCiAgICAgICAgCiAgICBkZWYgaW5wdXRfZGF0YShzZWxmKToKICAgICAgICBwcmludCgiPT09IOejuuWfuuawtOadqOmFuOmTgemFjeS9jeaVsOWSjOeos+WumuW4uOaVsOa1i+WumuWunumqjOaVsOaNruWkhOeQhiA9PT0iKQogICAgICAgIHByaW50KCJcbuivt+i+k+WFpTEx5Liq5pWw5o2u54K555qE5ZC45YWJ5bqm5YC8ICjlubPooYzlrp7pqowpOiIpCiAgICAgICAgcHJpbnQoIuagvOW8jzog54K5MeWunumqjDEg54K5MeWunumqjDIg54K5MuWunumqjDEg54K5MuWunumqjDIgLi4uIOeCuTEx5a6e6aqMMSDngrkxMeWunumqjDIiKQogICAgICAgIAogICAgICAgIGRhdGFfc3RyID0gaW5wdXQoIuivt+i+k+WFpTIy5Liq5ZC45YWJ5bqm5YC8OiAiKQogICAgICAgIGRhdGEgPSBsaXN0KG1hcChmbG9hdCwgZGF0YV9zdHIuc3BsaXQoKSkpCiAgICAgICAgCiAgICAgICAgaWYgbGVuKGRhdGEpICE9IDIyOgogICAgICAgICAgICByYWlzZSBWYWx1ZUVycm9yKCLlv4XpobvovpPlhaUyMuS4quWQuOWFieW6puWAvCEiKQogICAgICAgIAogICAgICAgIHNlbGYuYWJzb3JiYW5jZXNfYWxsID0gbnAuYXJyYXkoZGF0YSkucmVzaGFwZSgxMSwgMikKICAgICAgICBzZWxmLmFic29yYmFuY2VzID0gbnAubWVhbihzZWxmLmFic29yYmFuY2VzX2FsbCwgYXhpcz0xKQogICAgICAgIHNlbGYuYWJzb3JiYW5jZXNfc3RkID0gbnAuc3RkKHNlbGYuYWJzb3JiYW5jZXNfYWxsLCBheGlzPTEsIGRkb2Y9MSkKICAgICAgICAKICAgICAgICBwcmludCgiXG7mlbDmja7ovpPlhaXlrozmiJAhIikKICAgICAgICBwcmludChmIuaRqeWwlOWIhuaVsDoge3NlbGYubW9sZV9mcmFjdGlvbnN9IikKICAgICAgICBwcmludChmIuW5s+Wdh+WQuOWFieW6pjoge3NlbGYuYWJzb3JiYW5jZXN9IikKICAgICAgICAKICAgICAgICByZXR1cm4gc2VsZi5tb2xlX2ZyYWN0aW9ucywgc2VsZi5hYnNvcmJhbmNlcwogICAgCiAgICBkZWYgbGluZWFyX2ZpdChzZWxmLCB4X2RhdGEsIHlfZGF0YSk6CiAgICAgICAgQSA9IG5wLnZzdGFjayhbeF9kYXRhLCBucC5vbmVzKGxlbih4X2RhdGEpKV0pLlQKICAgICAgICBtLCBjID0gbnAubGluYWxnLmxzdHNxKEEsIHlfZGF0YSwgcmNvbmQ9Tm9uZSlbMF0KICAgICAgICByZXR1cm4gbSwgYwogICAgCiAgICBkZWYgY2FsY3VsYXRlX2ludGVyc2VjdGlvbihzZWxmLCBtMSwgYzEsIG0yLCBjMik6CiAgICAgICAgeF9pbnRlcnNlY3QgPSAoYzIgLSBjMSkgLyAobTEgLSBtMikKICAgICAgICB5X2ludGVyc2VjdCA9IG0xICogeF9pbnRlcnNlY3QgKyBjMQogICAgICAgIHJldHVybiB4X2ludGVyc2VjdCwgeV9pbnRlcnNlY3QKICAgIAogICAgZGVmIGZpbmRfY3VydmVfaW50ZXJzZWN0aW9uKHNlbGYsIHhfaW50ZXJzZWN0KToKICAgICAgICBmb3IgaSBpbiByYW5nZShsZW4oc2VsZi5tb2xlX2ZyYWN0aW9ucyktMSk6CiAgICAgICAgICAgIGlmIHNlbGYubW9sZV9mcmFjdGlvbnNbaV0gPD0geF9pbnRlcnNlY3QgPD0gc2VsZi5tb2xlX2ZyYWN0aW9uc1tpKzFdOgogICAgICAgICAgICAgICAgeDEsIHgyID0gc2VsZi5tb2xlX2ZyYWN0aW9uc1tpXSwgc2VsZi5tb2xlX2ZyYWN0aW9uc1tpKzFdCiAgICAgICAgICAgICAgICB5MSwgeTIgPSBzZWxmLmFic29yYmFuY2VzW2ldLCBzZWxmLmFic29yYmFuY2VzW2krMV0KICAgICAgICAgICAgICAgIHlfY3VydmUgPSB5MSArICh5MiAtIHkxKSAqICh4X2ludGVyc2VjdCAtIHgxKSAvICh4MiAtIHgxKQogICAgICAgICAgICAgICAgcmV0dXJuIHlfY3VydmUKICAgICAgICByZXR1cm4gc2VsZi5hYnNvcmJhbmNlc1tucC5hcmdtYXgoc2VsZi5hYnNvcmJhbmNlcyldCiAgICAKICAgIGRlZiBjYWxjdWxhdGVfYWNpZF9lZmZlY3Qoc2VsZiwgcEg9Mi41KToKICAgICAgICBIX3BsdXMgPSAxMCoqKC1wSCkKICAgICAgICBLMiA9IDEwKiooLTIuNikKICAgICAgICBLMyA9IDEwKiooLTExLjcpCiAgICAgICAgCiAgICAgICAgYWxwaGEgPSAxICsgSF9wbHVzL0szICsgSF9wbHVzKioyLyhLMipLMykKICAgICAgICBsZ19hbHBoYSA9IG5wLmxvZzEwKGFscGhhKQogICAgICAgIAogICAgICAgIHJldHVybiBsZ19hbHBoYQogICAgCiAgICBkZWYgYW5hbHl6ZV9kYXRhKHNlbGYpOgogICAgICAgIGlmIHNlbGYuYWJzb3JiYW5jZXMgaXMgTm9uZToKICAgICAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigi6K+35YWI6L6T5YWl5pWw5o2uISIpCiAgICAgICAgCiAgICAgICAgbGVmdF9pbmRpY2VzID0gWzAsIDEsIDJdCiAgICAgICAgcmlnaHRfaW5kaWNlcyA9IFs4LCA5LCAxMF0KICAgICAgICAKICAgICAgICBtX2xlZnQsIGNfbGVmdCA9IHNlbGYubGluZWFyX2ZpdCgKICAgICAgICAgICAgc2VsZi5tb2xlX2ZyYWN0aW9uc1tsZWZ0X2luZGljZXNdLCAKICAgICAgICAgICAgc2VsZi5hYnNvcmJhbmNlc1tsZWZ0X2luZGljZXNdCiAgICAgICAgKQogICAgICAgIAogICAgICAgIG1fcmlnaHQsIGNfcmlnaHQgPSBzZWxmLmxpbmVhcl9maXQoCiAgICAgICAgICAgIHNlbGYubW9sZV9mcmFjdGlvbnNbcmlnaHRfaW5kaWNlc10sIAogICAgICAgICAgICBzZWxmLmFic29yYmFuY2VzW3JpZ2h0X2luZGljZXNdCiAgICAgICAgKQogICAgICAgIAogICAgICAgIHhfaW50ZXJzZWN0LCBEMSA9IHNlbGYuY2FsY3VsYXRlX2ludGVyc2VjdGlvbihtX2xlZnQsIGNfbGVmdCwgbV9yaWdodCwgY19yaWdodCkKICAgICAgICAKICAgICAgICBEMiA9IHNlbGYuZmluZF9jdXJ2ZV9pbnRlcnNlY3Rpb24oeF9pbnRlcnNlY3QpCiAgICAgICAgCiAgICAgICAgY29vcmRpbmF0aW9uX251bWJlciA9IHhfaW50ZXJzZWN0IC8gKDEgLSB4X2ludGVyc2VjdCkKICAgICAgICAKICAgICAgICBpZiBEMSA+IDA6CiAgICAgICAgICAgIGRpc3NvY2lhdGlvbl9kZWdyZWUgPSAoRDEgLSBEMikgLyBEMQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGRpc3NvY2lhdGlvbl9kZWdyZWUgPSAwCiAgICAgICAgCiAgICAgICAgZmVfaW5pdGlhbF9tb2xlcyA9IHNlbGYuZmVfY29uY2VudHJhdGlvbiAqICgxIC0geF9pbnRlcnNlY3QpICogKHNlbGYudG90YWxfbWl4aW5nX3ZvbHVtZSAvIDIpIC8gMTAwMAogICAgICAgIGNvbXBsZXhfY29uY2VudHJhdGlvbiA9IGZlX2luaXRpYWxfbW9sZXMgLyAoc2VsZi50b3RhbF92b2x1bWUgLyAxMDAwKQogICAgICAgIAogICAgICAgIGlmIGRpc3NvY2lhdGlvbl9kZWdyZWUgPiAwIGFuZCBkaXNzb2NpYXRpb25fZGVncmVlIDwgMToKICAgICAgICAgICAgYXBwYXJlbnRfY29uc3RhbnQgPSAoMSAtIGRpc3NvY2lhdGlvbl9kZWdyZWUpIC8gKGNvbXBsZXhfY29uY2VudHJhdGlvbiAqIGRpc3NvY2lhdGlvbl9kZWdyZWUqKjIpCiAgICAgICAgICAgIAogICAgICAgICAgICBsZ19hbHBoYSA9IHNlbGYuY2FsY3VsYXRlX2FjaWRfZWZmZWN0KCkKICAgICAgICAgICAgc3RhYmlsaXR5X2NvbnN0YW50ID0gYXBwYXJlbnRfY29uc3RhbnQgKiAoMTAqKmxnX2FscGhhKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHN0YWJpbGl0eV9jb25zdGFudCA9IGZsb2F0KCdpbmYnKQogICAgICAgICAgICBhcHBhcmVudF9jb25zdGFudCA9IDAKICAgICAgICAKICAgICAgICByZXN1bHRzID0gewogICAgICAgICAgICAnRDEnOiBEMSwKICAgICAgICAgICAgJ0QyJzogRDIsCiAgICAgICAgICAgICd4X2ludGVyc2VjdCc6IHhfaW50ZXJzZWN0LAogICAgICAgICAgICAnY29vcmRpbmF0aW9uX251bWJlcic6IGNvb3JkaW5hdGlvbl9udW1iZXIsCiAgICAgICAgICAgICdkaXNzb2NpYXRpb25fZGVncmVlJzogZGlzc29jaWF0aW9uX2RlZ3JlZSwKICAgICAgICAgICAgJ3N0YWJpbGl0eV9jb25zdGFudCc6IHN0YWJpbGl0eV9jb25zdGFudCwKICAgICAgICAgICAgJ2FwcGFyZW50X2NvbnN0YW50JzogYXBwYXJlbnRfY29uc3RhbnQsCiAgICAgICAgICAgICdsZ19hbHBoYSc6IGxnX2FscGhhLAogICAgICAgICAgICAnbGVmdF9maXQnOiAobV9sZWZ0LCBjX2xlZnQpLAogICAgICAgICAgICAncmlnaHRfZml0JzogKG1fcmlnaHQsIGNfcmlnaHQpLAogICAgICAgICAgICAnY29tcGxleF9jb25jZW50cmF0aW9uJzogY29tcGxleF9jb25jZW50cmF0aW9uCiAgICAgICAgfQogICAgICAgIAogICAgICAgIHJldHVybiByZXN1bHRzCiAgICAKICAgIGRlZiBwbG90X3Jlc3VsdHMoc2VsZiwgcmVzdWx0cyk6CiAgICAgICAgaWYgc2VsZi5hYnNvcmJhbmNlcyBpcyBOb25lOgogICAgICAgICAgICByYWlzZSBWYWx1ZUVycm9yKCLor7flhYjovpPlhaXmlbDmja4hIikKICAgICAgICAKICAgICAgICBwbHQuZmlndXJlKGZpZ3NpemU9KDEyLCA4KSkKICAgICAgICAKICAgICAgICBjb25maWRlbmNlX3NpemVzID0gMTAwICsgMjAwICogKDEgLyAoc2VsZi5hYnNvcmJhbmNlc19zdGQgKyAwLjAwMSkgLyBucC5tYXgoMSAvIChzZWxmLmFic29yYmFuY2VzX3N0ZCArIDAuMDAxKSkpCiAgICAgICAgcGx0LnNjYXR0ZXIoc2VsZi5tb2xlX2ZyYWN0aW9ucywgc2VsZi5hYnNvcmJhbmNlcywgcz1jb25maWRlbmNlX3NpemVzLCBhbHBoYT0wLjcsIGNvbG9yPSdibHVlJywgbGFiZWw9J+WunumqjOaVsOaNrueCuScpCiAgICAgICAgCiAgICAgICAgZm9yIGksICh4LCB5KSBpbiBlbnVtZXJhdGUoemlwKHNlbGYubW9sZV9mcmFjdGlvbnMsIHNlbGYuYWJzb3JiYW5jZXMpKToKICAgICAgICAgICAgcGx0LmFubm90YXRlKGYne3k6LjNmfScsICh4LCB5KSwgdGV4dGNvb3Jkcz0ib2Zmc2V0IHBvaW50cyIsIHh5dGV4dD0oMCwxMCksIGhhPSdjZW50ZXInLCBmb250c2l6ZT05KQogICAgICAgIAogICAgICAgIGlmIGxlbihzZWxmLm1vbGVfZnJhY3Rpb25zKSA+IDM6CiAgICAgICAgICAgIHNwbGluZSA9IFVuaXZhcmlhdGVTcGxpbmUoc2VsZi5tb2xlX2ZyYWN0aW9ucywgc2VsZi5hYnNvcmJhbmNlcywgcz0wLjAwMSkKICAgICAgICAgICAgeF9zbW9vdGggPSBucC5saW5zcGFjZSgwLCAxLCAyMDApCiAgICAgICAgICAgIHlfc21vb3RoID0gc3BsaW5lKHhfc21vb3RoKQogICAgICAgICAgICBwbHQucGxvdCh4X3Ntb290aCwgeV9zbW9vdGgsICdiLScsIGFscGhhPTAuNywgbGluZXdpZHRoPTIsIGxhYmVsPSflrp7pqozmm7Lnur8nKQogICAgICAgIAogICAgICAgIHhfbGVmdCA9IG5wLmFycmF5KFswLjAsIHJlc3VsdHNbJ3hfaW50ZXJzZWN0J11dKQogICAgICAgIHhfcmlnaHQgPSBucC5hcnJheShbcmVzdWx0c1sneF9pbnRlcnNlY3QnXSwgMS4wXSkKICAgICAgICAKICAgICAgICBtX2xlZnQsIGNfbGVmdCA9IHJlc3VsdHNbJ2xlZnRfZml0J10KICAgICAgICBtX3JpZ2h0LCBjX3JpZ2h0ID0gcmVzdWx0c1sncmlnaHRfZml0J10KICAgICAgICAKICAgICAgICB4X2ZpdF9sZWZ0ID0gbnAubGluc3BhY2UoMC4wLCByZXN1bHRzWyd4X2ludGVyc2VjdCddLCA1MCkKICAgICAgICB5X2ZpdF9sZWZ0ID0gbV9sZWZ0ICogeF9maXRfbGVmdCArIGNfbGVmdAogICAgICAgIAogICAgICAgIHhfZml0X3JpZ2h0ID0gbnAubGluc3BhY2UocmVzdWx0c1sneF9pbnRlcnNlY3QnXSwgMS4wLCA1MCkKICAgICAgICB5X2ZpdF9yaWdodCA9IG1fcmlnaHQgKiB4X2ZpdF9yaWdodCArIGNfcmlnaHQKICAgICAgICAKICAgICAgICBwbHQucGxvdCh4X2ZpdF9sZWZ0LCB5X2ZpdF9sZWZ0LCAnci0tJywgbGluZXdpZHRoPTIsIGxhYmVsPSflt6bkvqfmi5/lkIjnm7Tnur8nKQogICAgICAgIHBsdC5wbG90KHhfZml0X3JpZ2h0LCB5X2ZpdF9yaWdodCwgJ2ctLScsIGxpbmV3aWR0aD0yLCBsYWJlbD0n5Y+z5L6n5ouf5ZCI55u057q/JykKICAgICAgICAKICAgICAgICBwbHQudGV4dCgwLjEsIDAuMSwgZiflt6bkvqc6IHkgPSB7bV9sZWZ0Oi4zZn14ICsge2NfbGVmdDouM2Z9JywgdHJhbnNmb3JtPXBsdC5nY2EoKS50cmFuc0F4ZXMsIGZvbnRzaXplPTEwLCBjb2xvcj0ncmVkJykKICAgICAgICBwbHQudGV4dCgwLjEsIDAuMDUsIGYn5Y+z5L6nOiB5ID0ge21fcmlnaHQ6LjNmfXggKyB7Y19yaWdodDouM2Z9JywgdHJhbnNmb3JtPXBsdC5nY2EoKS50cmFuc0F4ZXMsIGZvbnRzaXplPTEwLCBjb2xvcj0nZ3JlZW4nKQogICAgICAgIAogICAgICAgIHBsdC5heHZsaW5lKHg9cmVzdWx0c1sneF9pbnRlcnNlY3QnXSwgY29sb3I9J2dyYXknLCBsaW5lc3R5bGU9JzonLCBhbHBoYT0wLjcsIGxhYmVsPWYn5Lqk54K5WOWdkOaghzoge3Jlc3VsdHNbInhfaW50ZXJzZWN0Il06LjNmfScpCiAgICAgICAgCiAgICAgICAgcGx0LnNjYXR0ZXIoW3Jlc3VsdHNbJ3hfaW50ZXJzZWN0J11dLCBbcmVzdWx0c1snRDEnXV0sIGNvbG9yPSdyZWQnLCBzPTEwMCwgbGFiZWw9J+eQhuiuuuS6pOeCuScpCiAgICAgICAgcGx0LnNjYXR0ZXIoW3Jlc3VsdHNbJ3hfaW50ZXJzZWN0J11dLCBbcmVzdWx0c1snRDInXV0sIGNvbG9yPSdncmVlbicsIHM9MTAwLCBsYWJlbD0n5a6e6ZmF5Lqk54K5JykKICAgICAgICAKICAgICAgICBwbHQuYW5ub3RhdGUoZifnkIborro6IHtyZXN1bHRzWyJEMSJdOi4zZn0nLCAKICAgICAgICAgICAgICAgICAgICAocmVzdWx0c1sneF9pbnRlcnNlY3QnXSwgcmVzdWx0c1snRDEnXSksIAogICAgICAgICAgICAgICAgICAgIHRleHRjb29yZHM9Im9mZnNldCBwb2ludHMiLCB4eXRleHQ9KDEwLDEwKSwgaGE9J2xlZnQnLCBmb250c2l6ZT05LCBjb2xvcj0ncmVkJykKICAgICAgICAKICAgICAgICBwbHQuYW5ub3RhdGUoZiflrp7pmYU6IHtyZXN1bHRzWyJEMiJdOi4zZn0nLCAKICAgICAgICAgICAgICAgICAgICAocmVzdWx0c1sneF9pbnRlcnNlY3QnXSwgcmVzdWx0c1snRDInXSksIAogICAgICAgICAgICAgICAgICAgIHRleHRjb29yZHM9Im9mZnNldCBwb2ludHMiLCB4eXRleHQ9KDEwLC0xNSksIGhhPSdsZWZ0JywgZm9udHNpemU9OSwgY29sb3I9J2dyZWVuJykKICAgICAgICAKICAgICAgICBwbHQueGxhYmVsKCfno7rln7rmsLTmnajphbjnmoTmkanlsJTliIbmlbAnKQogICAgICAgIHBsdC55bGFiZWwoJ+WQuOWFieW6piAoQSknKQogICAgICAgIAogICAgICAgIHBsdC50aXRsZSgn56O65Z+65rC05p2o6YW46ZOBKOKFoinphY3lkIjniannu4TmiJDkuI7nqLPlrprluLjmlbDmtYvlrprnu5Pmnpzlm74nLCBmb250c2l6ZT0yMCwgcGFkPTIwKQogICAgICAgIAogICAgICAgIHBsdC50ZXh0KDAuOTgsIDAuOTgsICLnlLHkvZXlkIzlrabkuI5E6ICB5biI6IGU5ZCI5Yi25L2cIiwgCiAgICAgICAgICAgICAgICB0cmFuc2Zvcm09cGx0LmdjYSgpLnRyYW5zQXhlcywKICAgICAgICAgICAgICAgIGZvbnRzaXplPTYsIGNvbG9yPSdncmF5JywgCiAgICAgICAgICAgICAgICBoYT0ncmlnaHQnLCB2YT0ndG9wJywgCiAgICAgICAgICAgICAgICBiYm94PWRpY3QoYm94c3R5bGU9InJvdW5kLHBhZD0wLjIiLCBmYWNlY29sb3I9IndoaXRlIiwgYWxwaGE9MC44KSkKICAgICAgICAKICAgICAgICB0ZXh0c3RyID0gJ1xuJy5qb2luKFsKICAgICAgICAgICAgZifphY3lkIjniannu4TmiJA6IE1Me3Jlc3VsdHNbImNvb3JkaW5hdGlvbl9udW1iZXIiXTouMWZ9JywKICAgICAgICAgICAgZifkuqTngrlY5Z2Q5qCHOiB7cmVzdWx0c1sieF9pbnRlcnNlY3QiXTouM2Z9JywKICAgICAgICAgICAgZifop6PnprvluqY6IHtyZXN1bHRzWyJkaXNzb2NpYXRpb25fZGVncmVlIl06LjRmfScsCiAgICAgICAgICAgIGYn56iz5a6a5bi45pWwOiB7cmVzdWx0c1sic3RhYmlsaXR5X2NvbnN0YW50Il06LjJlfSBML21vbCcKICAgICAgICBdKQogICAgICAgIHByb3BzID0gZGljdChib3hzdHlsZT0ncm91bmQnLCBmYWNlY29sb3I9J3doZWF0JywgYWxwaGE9MC44KQogICAgICAgIHBsdC50ZXh0KDAuNjUsIDAuMjUsIHRleHRzdHIsIHRyYW5zZm9ybT1wbHQuZ2NhKCkudHJhbnNBeGVzLCBmb250c2l6ZT0xMiwKICAgICAgICAgICAgICAgIHZlcnRpY2FsYWxpZ25tZW50PSd0b3AnLCBiYm94PXByb3BzKQogICAgICAgIAogICAgICAgIHBsdC5ncmlkKFRydWUsIGFscGhhPTAuMykKICAgICAgICBwbHQubGVnZW5kKGxvYz0ndXBwZXIgbGVmdCcpCiAgICAgICAgcGx0LnRpZ2h0X2xheW91dCgpCiAgICAgICAgcGx0LnNob3coKQogICAgICAgIAogICAgICAgIHJldHVybiBwbHQuZ2NmKCkKICAgIAogICAgZGVmIHJ1bl9hbmFseXNpcyhzZWxmKToKICAgICAgICB0cnk6CiAgICAgICAgICAgIHNlbGYuaW5wdXRfZGF0YSgpCiAgICAgICAgICAgIHJlc3VsdHMgPSBzZWxmLmFuYWx5emVfZGF0YSgpCiAgICAgICAgICAgIAogICAgICAgICAgICBwcmludCgiXG4iICsgIj0iKjYwKQogICAgICAgICAgICBwcmludCgi5a6e6aqM57uT5p6c5YiG5p6QIikKICAgICAgICAgICAgcHJpbnQoIj0iKjYwKQogICAgICAgICAgICBwcmludChmIumFjeWQiOeJqee7hOaIkDogTUx7cmVzdWx0c1snY29vcmRpbmF0aW9uX251bWJlciddOi4xZn0iKQogICAgICAgICAgICBwcmludChmIuS6pOeCuVjlnZDmoIc6IHtyZXN1bHRzWyd4X2ludGVyc2VjdCddOi4zZn0iKQogICAgICAgICAgICBwcmludChmIuino+emu+W6pjoge3Jlc3VsdHNbJ2Rpc3NvY2lhdGlvbl9kZWdyZWUnXTouNGZ9IikKICAgICAgICAgICAgcHJpbnQoZiLooajop4LnqLPlrprluLjmlbA6IHtyZXN1bHRzWydhcHBhcmVudF9jb25zdGFudCddOi4yZX0gTC9tb2wiKQogICAgICAgICAgICBwcmludChmIumFuOaViOW6lOezu+aVsCBsZ86xOiB7cmVzdWx0c1snbGdfYWxwaGEnXTouM2Z9IikKICAgICAgICAgICAgcHJpbnQoZiLng63lipvlrabnqLPlrprluLjmlbA6IHtyZXN1bHRzWydzdGFiaWxpdHlfY29uc3RhbnQnXTouMmV9IEwvbW9sIikKICAgICAgICAgICAgCiAgICAgICAgICAgIHByaW50KCJcbueUn+aIkOWbvuihqOS4rS4uLiIpCiAgICAgICAgICAgIHNlbGYucGxvdF9yZXN1bHRzKHJlc3VsdHMpCiAgICAgICAgICAgIHByaW50KCLlm77ooajlt7LmmL7npLrvvIzor7fmn6XnnIvlvLnlh7rnqpflj6PjgIIiKQogICAgICAgICAgICAKICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgICAgIHByaW50KGYi6ZSZ6K+vOiB7ZX0iKQogICAgICAgICAgICBwcmludCgi6K+35qOA5p+l6L6T5YWl5pWw5o2u5qC85byP5piv5ZCm5q2j56Gu44CCIikKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBleHBlcmltZW50ID0gU3VsZm9zYWxpY3lsYXRlSXJvbkV4cGVyaW1lbnQoKQogICAgZXhwZXJpbWVudC5ydW5fYW5hbHlzaXMoKQ==