Hand-Off Refactor Pt. 1
This commit is contained in:
parent
d60411b7b4
commit
8e699b199d
|
|
@ -1,5 +1,5 @@
|
||||||
from fn.op import foldr
|
from fn.op import foldr
|
||||||
from SimCAD.utils.configuration import dict_elemwise_sum
|
from SimCAD.configuration.utils.behaviorAggregation import dict_elemwise_sum
|
||||||
|
|
||||||
|
|
||||||
configs = []
|
configs = []
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
from functools import reduce
|
||||||
|
import pandas as pd
|
||||||
|
from SimCAD.utils import key_filter
|
||||||
|
|
||||||
|
class Identity:
|
||||||
|
def __init__(self, behavior_id={'indentity': 0}):
|
||||||
|
self.beh_id_return_val = behavior_id
|
||||||
|
|
||||||
|
def b_identity(self, step, sL, s):
|
||||||
|
return self.beh_id_return_val
|
||||||
|
|
||||||
|
def behavior_identity(self, k):
|
||||||
|
return self.b_identity
|
||||||
|
|
||||||
|
def no_state_identity(self, step, sL, s, _input):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def state_identity(self, k):
|
||||||
|
return lambda step, sL, s, _input: (k, s[k])
|
||||||
|
|
||||||
|
def apply_identity_funcs(self, identity, df, cols):
|
||||||
|
def fillna_with_id_func(identity, df, col):
|
||||||
|
return df[[col]].fillna(value=identity(col))
|
||||||
|
|
||||||
|
return list(map(lambda col: fillna_with_id_func(identity, df, col), cols))
|
||||||
|
|
||||||
|
|
||||||
|
class Processor:
|
||||||
|
|
||||||
|
def __init__(self, id=Identity()):
|
||||||
|
self.id = id
|
||||||
|
self.b_identity = id.b_identity
|
||||||
|
self.behavior_identity = id.behavior_identity
|
||||||
|
self.no_state_identity = id.no_state_identity
|
||||||
|
self.state_identity = id.state_identity
|
||||||
|
self.apply_identity_funcs = id.apply_identity_funcs
|
||||||
|
|
||||||
|
# Make returntype chosen by user. Must Classify Configs
|
||||||
|
def create_matrix_field(self, mechanisms, key):
|
||||||
|
if key == 'states':
|
||||||
|
identity = self.state_identity
|
||||||
|
else:
|
||||||
|
identity = self.behavior_identity
|
||||||
|
df = pd.DataFrame(key_filter(mechanisms, key))
|
||||||
|
col_list = self.apply_identity_funcs(identity, df, list(df.columns))
|
||||||
|
if len(col_list) != 0:
|
||||||
|
return reduce((lambda x, y: pd.concat([x, y], axis=1)), col_list)
|
||||||
|
else:
|
||||||
|
return pd.DataFrame({'empty': []})
|
||||||
|
|
||||||
|
# Maybe Refactor to only use dictionary BUT I used dfs to fill NAs. Perhaps fill
|
||||||
|
def generate_config(self, state_dict, mechanisms, exo_proc):
|
||||||
|
|
||||||
|
# include False / False case
|
||||||
|
def no_update_handler(bdf, sdf):
|
||||||
|
if (bdf.empty == False) and (sdf.empty == True):
|
||||||
|
bdf_values = bdf.values.tolist()
|
||||||
|
sdf_values = [[self.no_state_identity] * len(bdf_values) for m in range(len(mechanisms))]
|
||||||
|
return sdf_values, bdf_values
|
||||||
|
elif (bdf.empty == True) and (sdf.empty == False):
|
||||||
|
sdf_values = sdf.values.tolist()
|
||||||
|
bdf_values = [[self.b_identity] * len(sdf_values) for m in range(len(mechanisms))]
|
||||||
|
return sdf_values, bdf_values
|
||||||
|
else:
|
||||||
|
sdf_values = sdf.values.tolist()
|
||||||
|
bdf_values = bdf.values.tolist()
|
||||||
|
return sdf_values, bdf_values
|
||||||
|
|
||||||
|
def only_ep_handler(state_dict):
|
||||||
|
sdf_functions = [
|
||||||
|
lambda step, sL, s, _input: (k, v) for k, v in zip(state_dict.keys(), state_dict.values())
|
||||||
|
]
|
||||||
|
sdf_values = [sdf_functions]
|
||||||
|
bdf_values = [[self.b_identity] * len(sdf_values)]
|
||||||
|
return sdf_values, bdf_values
|
||||||
|
|
||||||
|
# zipped_list = []
|
||||||
|
if len(mechanisms) != 0:
|
||||||
|
bdf = self.create_matrix_field(mechanisms, 'behaviors')
|
||||||
|
sdf = self.create_matrix_field(mechanisms, 'states')
|
||||||
|
sdf_values, bdf_values = no_update_handler(bdf, sdf)
|
||||||
|
zipped_list = list(zip(sdf_values, bdf_values))
|
||||||
|
else:
|
||||||
|
sdf_values, bdf_values = only_ep_handler(state_dict)
|
||||||
|
zipped_list = list(zip(sdf_values, bdf_values))
|
||||||
|
|
||||||
|
return list(map(lambda x: (x[0] + exo_proc, x[1]), zipped_list))
|
||||||
|
|
@ -1,7 +1,20 @@
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from fn.func import curried
|
from fn.func import curried
|
||||||
from fn.op import foldr
|
import pandas as pd
|
||||||
|
|
||||||
|
class TensorFieldReport:
|
||||||
|
def __init__(self, config_proc):
|
||||||
|
self.config_proc = config_proc
|
||||||
|
|
||||||
|
# dont for-loop to apply exo_procs, use exo_proc struct
|
||||||
|
def create_tensor_field(self, mechanisms, exo_proc, keys=['behaviors', 'states']):
|
||||||
|
dfs = [self.config_proc.create_matrix_field(mechanisms, k) for k in keys]
|
||||||
|
df = pd.concat(dfs, axis=1)
|
||||||
|
for es, i in zip(exo_proc, range(len(exo_proc))):
|
||||||
|
df['es' + str(i + 1)] = es
|
||||||
|
df['m'] = df.index + 1
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
def bound_norm_random(rng, low, high):
|
def bound_norm_random(rng, low, high):
|
||||||
|
|
@ -45,52 +58,3 @@ def exo_update_per_ts(ep):
|
||||||
else:
|
else:
|
||||||
return (y, s[y])
|
return (y, s[y])
|
||||||
return {es: ep_decorator(f, es) for es, f in ep.items()}
|
return {es: ep_decorator(f, es) for es, f in ep.items()}
|
||||||
|
|
||||||
|
|
||||||
def print_fwd(x):
|
|
||||||
print(x)
|
|
||||||
return x
|
|
||||||
|
|
||||||
|
|
||||||
def get_base_value(datatype):
|
|
||||||
if datatype is str:
|
|
||||||
return ''
|
|
||||||
elif datatype is int:
|
|
||||||
return 0
|
|
||||||
elif datatype is list:
|
|
||||||
return []
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def behavior_to_dict(v):
|
|
||||||
return dict(list(zip(map(lambda n: 'b' + str(n + 1), list(range(len(v)))), v)))
|
|
||||||
|
|
||||||
|
|
||||||
add = lambda a, b: a + b
|
|
||||||
|
|
||||||
|
|
||||||
@curried
|
|
||||||
def foldr_dict_vals(f, d):
|
|
||||||
return foldr(f)(list(d.values()))
|
|
||||||
|
|
||||||
|
|
||||||
def sum_dict_values():
|
|
||||||
return foldr_dict_vals(add)
|
|
||||||
|
|
||||||
# AttributeError: 'int' object has no attribute 'keys'
|
|
||||||
# config7c
|
|
||||||
@curried
|
|
||||||
def dict_op(f, d1, d2):
|
|
||||||
def set_base_value(target_dict, source_dict, key):
|
|
||||||
if key not in target_dict:
|
|
||||||
return get_base_value(type(source_dict[key]))
|
|
||||||
else:
|
|
||||||
return target_dict[key]
|
|
||||||
|
|
||||||
key_set = set(list(d1.keys()) + list(d2.keys()))
|
|
||||||
|
|
||||||
return {k: f(set_base_value(d1, d2, k), set_base_value(d2, d1, k)) for k in key_set}
|
|
||||||
|
|
||||||
|
|
||||||
def dict_elemwise_sum():
|
|
||||||
return dict_op(add)
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
from fn.op import foldr
|
||||||
|
from fn.func import curried
|
||||||
|
|
||||||
|
|
||||||
|
def get_base_value(datatype):
|
||||||
|
if datatype is str:
|
||||||
|
return ''
|
||||||
|
elif datatype is int:
|
||||||
|
return 0
|
||||||
|
elif datatype is list:
|
||||||
|
return []
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def behavior_to_dict(v):
|
||||||
|
return dict(list(zip(map(lambda n: 'b' + str(n + 1), list(range(len(v)))), v)))
|
||||||
|
|
||||||
|
|
||||||
|
add = lambda a, b: a + b
|
||||||
|
|
||||||
|
|
||||||
|
@curried
|
||||||
|
def foldr_dict_vals(f, d):
|
||||||
|
return foldr(f)(list(d.values()))
|
||||||
|
|
||||||
|
|
||||||
|
def sum_dict_values():
|
||||||
|
return foldr_dict_vals(add)
|
||||||
|
|
||||||
|
# AttributeError: 'int' object has no attribute 'keys'
|
||||||
|
# config7c
|
||||||
|
@curried
|
||||||
|
def dict_op(f, d1, d2):
|
||||||
|
def set_base_value(target_dict, source_dict, key):
|
||||||
|
if key not in target_dict:
|
||||||
|
return get_base_value(type(source_dict[key]))
|
||||||
|
else:
|
||||||
|
return target_dict[key]
|
||||||
|
|
||||||
|
key_set = set(list(d1.keys()) + list(d2.keys()))
|
||||||
|
|
||||||
|
return {k: f(set_base_value(d1, d2, k), set_base_value(d2, d1, k)) for k in key_set}
|
||||||
|
|
||||||
|
|
||||||
|
def dict_elemwise_sum():
|
||||||
|
return dict_op(add)
|
||||||
|
|
||||||
|
|
||||||
|
# class BehaviorAggregation:
|
||||||
|
|
@ -2,8 +2,8 @@ from pathos.multiprocessing import ProcessingPool as Pool
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from SimCAD.utils import flatten
|
from SimCAD.utils import flatten
|
||||||
from SimCAD.utils.ui import create_tensor_field
|
from SimCAD.configuration import Processor
|
||||||
from SimCAD.utils.configProcessor import generate_config
|
from SimCAD.configuration.utils import TensorFieldReport
|
||||||
from SimCAD.engine.simulation import Executor as SimExecutor
|
from SimCAD.engine.simulation import Executor as SimExecutor
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -50,6 +50,9 @@ class Executor:
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
|
|
||||||
|
config_proc = Processor()
|
||||||
|
create_tensor_field = TensorFieldReport(config_proc).create_tensor_field
|
||||||
|
|
||||||
print(self.exec_context+": "+str(self.configs))
|
print(self.exec_context+": "+str(self.configs))
|
||||||
states_lists, Ts, Ns, eps, configs_structs, env_processes_list, mechanisms, simulation_execs = \
|
states_lists, Ts, Ns, eps, configs_structs, env_processes_list, mechanisms, simulation_execs = \
|
||||||
[], [], [], [], [], [], [], []
|
[], [], [], [], [], [], [], []
|
||||||
|
|
@ -59,7 +62,7 @@ class Executor:
|
||||||
Ts.append(x.sim_config['T'])
|
Ts.append(x.sim_config['T'])
|
||||||
Ns.append(x.sim_config['N'])
|
Ns.append(x.sim_config['N'])
|
||||||
eps.append(list(x.exogenous_states.values()))
|
eps.append(list(x.exogenous_states.values()))
|
||||||
configs_structs.append(generate_config(x.state_dict, x.mechanisms, eps[config_idx]))
|
configs_structs.append(config_proc.generate_config(x.state_dict, x.mechanisms, eps[config_idx]))
|
||||||
env_processes_list.append(x.env_processes)
|
env_processes_list.append(x.env_processes)
|
||||||
mechanisms.append(x.mechanisms)
|
mechanisms.append(x.mechanisms)
|
||||||
simulation_execs.append(SimExecutor(x.behavior_ops).simulation)
|
simulation_execs.append(SimExecutor(x.behavior_ops).simulation)
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ from fn.op import foldr, call
|
||||||
import pprint
|
import pprint
|
||||||
pp = pprint.PrettyPrinter(indent=4)
|
pp = pprint.PrettyPrinter(indent=4)
|
||||||
|
|
||||||
|
|
||||||
class Executor:
|
class Executor:
|
||||||
def __init__(self, behavior_ops):
|
def __init__(self, behavior_ops):
|
||||||
self.behavior_ops = behavior_ops
|
self.behavior_ops = behavior_ops
|
||||||
|
|
||||||
|
|
||||||
# Data Type reduction
|
# Data Type reduction
|
||||||
def getBehaviorInput(self, step, sL, s, funcs):
|
def getBehaviorInput(self, step, sL, s, funcs):
|
||||||
|
|
||||||
|
|
@ -17,13 +17,11 @@ class Executor:
|
||||||
|
|
||||||
return foldr(call, getColResults(step, sL, s, funcs))(ops)
|
return foldr(call, getColResults(step, sL, s, funcs))(ops)
|
||||||
|
|
||||||
|
|
||||||
def apply_env_proc(self, env_processes, state_dict, step):
|
def apply_env_proc(self, env_processes, state_dict, step):
|
||||||
for state in state_dict.keys():
|
for state in state_dict.keys():
|
||||||
if state in list(env_processes.keys()):
|
if state in list(env_processes.keys()):
|
||||||
state_dict[state] = env_processes[state](step)(state_dict[state])
|
state_dict[state] = env_processes[state](step)(state_dict[state])
|
||||||
|
|
||||||
|
|
||||||
# remove / modify
|
# remove / modify
|
||||||
def exception_handler(self, f, m_step, sL, last_mut_obj, _input):
|
def exception_handler(self, f, m_step, sL, last_mut_obj, _input):
|
||||||
try:
|
try:
|
||||||
|
|
@ -32,7 +30,6 @@ class Executor:
|
||||||
print("Exception")
|
print("Exception")
|
||||||
return f(m_step, sL, sL[-2], _input)
|
return f(m_step, sL, sL[-2], _input)
|
||||||
|
|
||||||
|
|
||||||
def mech_step(self, m_step, sL, state_funcs, behavior_funcs, env_processes, t_step, run):
|
def mech_step(self, m_step, sL, state_funcs, behavior_funcs, env_processes, t_step, run):
|
||||||
last_in_obj = sL[-1]
|
last_in_obj = sL[-1]
|
||||||
|
|
||||||
|
|
@ -58,7 +55,6 @@ class Executor:
|
||||||
|
|
||||||
return sL
|
return sL
|
||||||
|
|
||||||
|
|
||||||
def mech_pipeline(self, states_list, configs, env_processes, t_step, run):
|
def mech_pipeline(self, states_list, configs, env_processes, t_step, run):
|
||||||
m_step = 0
|
m_step = 0
|
||||||
states_list_copy = deepcopy(states_list)
|
states_list_copy = deepcopy(states_list)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
|
def print_fwd(x):
|
||||||
|
print(x)
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
flatten = lambda l: [item for sublist in l for item in sublist]
|
flatten = lambda l: [item for sublist in l for item in sublist]
|
||||||
|
|
||||||
|
|
||||||
def flatmap(f, items):
|
def flatmap(f, items):
|
||||||
return list(map(f, items))
|
return list(map(f, items))
|
||||||
|
|
||||||
|
|
||||||
|
def key_filter(l, keyname):
|
||||||
|
return [v[keyname] for k, v in l.items()]
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
import pandas as pd
|
|
||||||
from functools import reduce
|
|
||||||
|
|
||||||
|
|
||||||
def no_state_identity(step, sL, s, _input):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def state_identity(k):
|
|
||||||
return lambda step, sL, s, _input: (k, s[k])
|
|
||||||
|
|
||||||
|
|
||||||
# Make returntype chosen by user. Must Classify Configs
|
|
||||||
def b_identity(step, sL, s):
|
|
||||||
return {'indentity': 0}
|
|
||||||
|
|
||||||
|
|
||||||
def behavior_identity(k):
|
|
||||||
return b_identity
|
|
||||||
|
|
||||||
|
|
||||||
def key_filter(mechanisms, keyname):
|
|
||||||
return [v[keyname] for k, v in mechanisms.items()]
|
|
||||||
|
|
||||||
|
|
||||||
def fillna_with_id_func(identity, df, col):
|
|
||||||
return df[[col]].fillna(value=identity(col))
|
|
||||||
|
|
||||||
|
|
||||||
def apply_identity_funcs(identity, df, cols):
|
|
||||||
return list(map(lambda col: fillna_with_id_func(identity, df, col), cols))
|
|
||||||
|
|
||||||
|
|
||||||
def create_matrix_field(mechanisms, key):
|
|
||||||
if key == 'states':
|
|
||||||
identity = state_identity
|
|
||||||
else:
|
|
||||||
identity = behavior_identity
|
|
||||||
df = pd.DataFrame(key_filter(mechanisms, key))
|
|
||||||
col_list = apply_identity_funcs(identity, df, list(df.columns))
|
|
||||||
if len(col_list) != 0:
|
|
||||||
return reduce((lambda x, y: pd.concat([x, y], axis=1)), col_list)
|
|
||||||
else:
|
|
||||||
return pd.DataFrame({'empty' : []})
|
|
||||||
|
|
||||||
|
|
||||||
# Maybe Refactor to only use dictionary BUT I used dfs to fill NAs. Perhaps fill
|
|
||||||
def generate_config(state_dict, mechanisms, exo_proc):
|
|
||||||
|
|
||||||
# include False / False case
|
|
||||||
def no_update_handler(bdf, sdf):
|
|
||||||
if (bdf.empty == False) and (sdf.empty == True):
|
|
||||||
bdf_values = bdf.values.tolist()
|
|
||||||
sdf_values = [[no_state_identity] * len(bdf_values) for m in range(len(mechanisms))]
|
|
||||||
return sdf_values, bdf_values
|
|
||||||
elif (bdf.empty == True) and (sdf.empty == False):
|
|
||||||
sdf_values = sdf.values.tolist()
|
|
||||||
bdf_values = [[b_identity] * len(sdf_values) for m in range(len(mechanisms))]
|
|
||||||
return sdf_values, bdf_values
|
|
||||||
else:
|
|
||||||
sdf_values = sdf.values.tolist()
|
|
||||||
bdf_values = bdf.values.tolist()
|
|
||||||
return sdf_values, bdf_values
|
|
||||||
|
|
||||||
def only_ep_handler(state_dict):
|
|
||||||
sdf_functions = [
|
|
||||||
lambda step, sL, s, _input: (k, v) for k, v in zip(state_dict.keys(), state_dict.values())
|
|
||||||
]
|
|
||||||
sdf_values = [sdf_functions]
|
|
||||||
bdf_values = [[b_identity] * len(sdf_values)]
|
|
||||||
return sdf_values, bdf_values
|
|
||||||
|
|
||||||
# zipped_list = []
|
|
||||||
if len(mechanisms) != 0:
|
|
||||||
bdf = create_matrix_field(mechanisms, 'behaviors')
|
|
||||||
sdf = create_matrix_field(mechanisms, 'states')
|
|
||||||
sdf_values, bdf_values = no_update_handler(bdf, sdf)
|
|
||||||
zipped_list = list(zip(sdf_values, bdf_values))
|
|
||||||
else:
|
|
||||||
sdf_values, bdf_values = only_ep_handler(state_dict)
|
|
||||||
zipped_list = list(zip(sdf_values, bdf_values))
|
|
||||||
|
|
||||||
return list(map(lambda x: (x[0] + exo_proc, x[1]), zipped_list))
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import pandas as pd
|
|
||||||
from SimCAD.utils.configProcessor import create_matrix_field
|
|
||||||
|
|
||||||
|
|
||||||
# dont for-loop to apply exo_procs, use exo_proc struct
|
|
||||||
def create_tensor_field(mechanisms, exo_proc, keys=['behaviors', 'states']):
|
|
||||||
dfs = [create_matrix_field(mechanisms, k) for k in keys]
|
|
||||||
df = pd.concat(dfs, axis=1)
|
|
||||||
for es, i in zip(exo_proc, range(len(exo_proc))):
|
|
||||||
df['es'+str(i+1)] = es
|
|
||||||
df['m'] = df.index + 1
|
|
||||||
return df
|
|
||||||
|
|
@ -2,7 +2,7 @@ from decimal import Decimal
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from SimCAD import Configuration, configs
|
from SimCAD import Configuration, configs
|
||||||
from SimCAD.utils.configuration import exo_update_per_ts, proc_trigger, bound_norm_random, \
|
from SimCAD.configuration import exo_update_per_ts, bound_norm_random, \
|
||||||
ep_time_step
|
ep_time_step
|
||||||
|
|
||||||
seed = {
|
seed = {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from decimal import Decimal
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from SimCAD import Configuration, configs
|
from SimCAD import Configuration, configs
|
||||||
from SimCAD.utils.configuration import exo_update_per_ts, proc_trigger, bound_norm_random, \
|
from SimCAD.configuration import exo_update_per_ts, bound_norm_random, \
|
||||||
ep_time_step
|
ep_time_step
|
||||||
|
|
||||||
seed = {
|
seed = {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import numpy as np
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from SimCAD import Configuration, configs
|
from SimCAD import Configuration, configs
|
||||||
from SimCAD.utils.configuration import exo_update_per_ts, proc_trigger, bound_norm_random, \
|
from SimCAD.configuration.utils import exo_update_per_ts, proc_trigger, bound_norm_random, \
|
||||||
ep_time_step
|
ep_time_step
|
||||||
|
|
||||||
seed = {
|
seed = {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import numpy as np
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from SimCAD import Configuration, configs
|
from SimCAD import Configuration, configs
|
||||||
from SimCAD.utils.configuration import exo_update_per_ts, proc_trigger, bound_norm_random, \
|
from SimCAD.configuration.utils import exo_update_per_ts, proc_trigger, bound_norm_random, \
|
||||||
ep_time_step
|
ep_time_step
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from decimal import Decimal
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from SimCAD import Configuration, configs
|
from SimCAD import Configuration, configs
|
||||||
from SimCAD.utils.configuration import exo_update_per_ts, proc_trigger, bound_norm_random, \
|
from SimCAD.configuration import exo_update_per_ts, proc_trigger, bound_norm_random, \
|
||||||
ep_time_step
|
ep_time_step
|
||||||
|
|
||||||
seed = {
|
seed = {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue