Skip to content
Snippets Groups Projects
Commit a704c1a1 authored by Denis Arrivault's avatar Denis Arrivault
Browse files

Adding io functions for Automata

parent 71219463
No related branches found
No related tags found
No related merge requests found
Showing
with 610 additions and 92 deletions
New BSD License
Copyright (c) DATE, The SCIKIT-SPLEARN developers.
Copyright (c) 2016-DATE, The SCIKIT-SPLEARN developers.
All rights reserved.
Redistribution and use in source and binary forms, with or without
......
######### COPYRIGHT #########
Copyright(c) DATE
Copyright(c) 2016-DATE
-----------------
* LabEx Archimède: http://labex-archimede.univ-amu.fr/
* Laboratoire d'Informatique Fondamentale : http://www.lif.univ-mrs.fr/
* Laboratoire d'Informatique et Systèmes : http://www.lis-lab.fr/
Contributors:
------------
* François Denis <francois.denis_AT_lif.univ-mrs.fr>
* Rémi Eyraud <remy.eyraud_AT_lif.univ-mrs.fr>
* Denis Arrivault <contact.dev_AT_lif.univ-mrs.fr>
* François Denis <francois.denis_AT_lis-lab.fr>
* Rémi Eyraud <remi.eyraud_AT_lis-lab.fr>
* Denis Arrivault <contact.dev_AT_lis-lab.fr>
* Dominique Benielli <dominique.benielli_AT_univ-amu.fr>
Description:
......
{"automaton": {"transitions": [{"numpy.ndarray": {"values": [[0.045121209595118526, -0.24038969827843812, 0.34944999592135073, -0.2811680730534614, -0.2140252337749723], [0.0692580056243754, -0.30062293462828926, 0.20641375368520323, -0.14960814319757162, -0.5580573163749147], [0.029801151921765866, -0.13866480809160275, 0.18362212572805375, -0.20969545230657946, -0.14481622025561033], [0.005699344003197941, -0.023385825120200612, -0.0660066537398175, 0.10749935271465784, -0.1510365460415996], [-0.020086551931478853, 0.09026347555230403, -0.005525585655539282, -0.03135531709030631, 0.24329022420477361]], "dtype": "float64"}}, {"numpy.ndarray": {"values": [[0.07744772079170577, 0.09007073705761888, -0.3047220063293004, 0.2767624549859125, 0.2028939603062788], [-0.0990298048367086, -0.08061846818727912, 0.2585317069225045, -0.12086330214608965, -0.11085207725068019], [-0.061710792028537555, -0.06244151779954693, 0.12007654564862041, 0.0025063746277926833, -0.15679674731455712], [-0.002736973749965318, -0.009005721984277385, -0.0004600329590919212, -0.008550426472005884, -0.05375464678968064], [0.030987327588710818, 0.03972680066723204, -0.049971133509102365, 0.0035769411874977224, 0.14182576205856343]], "dtype": "float64"}}, {"numpy.ndarray": {"values": [[-0.06791915236220329, -0.1135793765908807, 0.3795539260405443, -0.21784979894047046, -0.22977695089937855], [0.11596642335411368, 0.14914956804629215, -0.1335750837668692, -0.008916063072030857, 0.3484153673774847], [0.011730817547426591, 0.01927380053195553, 0.04142658345867102, -0.03534658856098166, 0.02316491010895659], [0.0073289110755416255, 0.005536509132796289, -0.02245608295066666, 0.03611543477693135, -0.03851433900140641], [-0.010589894686551481, -0.010626616553723545, -0.0005431056456618302, -0.02556747670016023, 0.04984888818929077]], "dtype": "float64"}}, {"numpy.ndarray": {"values": [[0.0727621142778044, -0.01571955768557908, 0.07428592814589835, -0.10369861539250237, 0.024753473688334762], [-0.05607105449779192, -0.08896207276035564, 0.2763822539752204, -0.23711255828386232, 0.07372294122304948], [-0.007391294007753285, -0.04874179796387578, -0.6291239733858517, 0.4681627652157708, 0.09251699239093028], [-0.007110224931878148, -0.056233177358980875, -0.3660665856762066, -0.013297798115226096, 0.649103317749269], [0.0023355150085570832, -0.021561151264484168, 0.09096243479437624, -0.3843882349306193, 0.6616477207948629]], "dtype": "float64"}}], "type": "classic", "nbL": 4, "nbS": 5, "initial": {"numpy.ndarray": {"values": [-0.0004934419970497477, 0.003063469710791532, -0.0440739320155803, -0.10777702616547354, -0.0866391379316936], "dtype": "float64"}}, "final": {"numpy.ndarray": {"values": [0.07757136847945034, -0.02422029400314446, -0.4468125366321277, 0.6277320840897538, -0.554674433356226], "dtype": "float64"}}}}
\ No newline at end of file
automaton:
final:
numpy.ndarray:
dtype: float64
values: [0.07757136847945034, -0.02422029400314446, -0.4468125366321277, 0.6277320840897538,
-0.554674433356226]
initial:
numpy.ndarray:
dtype: float64
values: [-0.0004934419970497477, 0.003063469710791532, -0.0440739320155803,
-0.10777702616547354, -0.0866391379316936]
nbL: 4
nbS: 5
transitions:
- numpy.ndarray:
dtype: float64
values:
- [0.045121209595118526, -0.24038969827843812, 0.34944999592135073, -0.2811680730534614,
-0.2140252337749723]
- [0.0692580056243754, -0.30062293462828926, 0.20641375368520323, -0.14960814319757162,
-0.5580573163749147]
- [0.029801151921765866, -0.13866480809160275, 0.18362212572805375, -0.20969545230657946,
-0.14481622025561033]
- [0.005699344003197941, -0.023385825120200612, -0.0660066537398175, 0.10749935271465784,
-0.1510365460415996]
- [-0.020086551931478853, 0.09026347555230403, -0.005525585655539282, -0.03135531709030631,
0.24329022420477361]
- numpy.ndarray:
dtype: float64
values:
- [0.07744772079170577, 0.09007073705761888, -0.3047220063293004, 0.2767624549859125,
0.2028939603062788]
- [-0.0990298048367086, -0.08061846818727912, 0.2585317069225045, -0.12086330214608965,
-0.11085207725068019]
- [-0.061710792028537555, -0.06244151779954693, 0.12007654564862041, 0.0025063746277926833,
-0.15679674731455712]
- [-0.002736973749965318, -0.009005721984277385, -0.0004600329590919212, -0.008550426472005884,
-0.05375464678968064]
- [0.030987327588710818, 0.03972680066723204, -0.049971133509102365, 0.0035769411874977224,
0.14182576205856343]
- numpy.ndarray:
dtype: float64
values:
- [-0.06791915236220329, -0.1135793765908807, 0.3795539260405443, -0.21784979894047046,
-0.22977695089937855]
- [0.11596642335411368, 0.14914956804629215, -0.1335750837668692, -0.008916063072030857,
0.3484153673774847]
- [0.011730817547426591, 0.01927380053195553, 0.04142658345867102, -0.03534658856098166,
0.02316491010895659]
- [0.0073289110755416255, 0.005536509132796289, -0.02245608295066666, 0.03611543477693135,
-0.03851433900140641]
- [-0.010589894686551481, -0.010626616553723545, -0.0005431056456618302, -0.02556747670016023,
0.04984888818929077]
- numpy.ndarray:
dtype: float64
values:
- [0.0727621142778044, -0.01571955768557908, 0.07428592814589835, -0.10369861539250237,
0.024753473688334762]
- [-0.05607105449779192, -0.08896207276035564, 0.2763822539752204, -0.23711255828386232,
0.07372294122304948]
- [-0.007391294007753285, -0.04874179796387578, -0.6291239733858517, 0.4681627652157708,
0.09251699239093028]
- [-0.007110224931878148, -0.056233177358980875, -0.3660665856762066, -0.013297798115226096,
0.649103317749269]
- [0.0023355150085570832, -0.021561151264484168, 0.09096243479437624, -0.3843882349306193,
0.6616477207948629]
type: classic
from splearn.automaton import Automaton
from graphviz import Source
input_file = 'simple_automata'
format = ['json', 'yaml']
for f in format:
A = Automaton.read(input_file + "." + f, format=f)
dot = A.get_dot(title = "Simple Automata")
src = Source(dot)
src.render(input_file + '.' + f + '.gv', view=True)
This diff is collapsed.
from splearn.datasets.base import load_data_sample
from splearn.tests.datasets.get_dataset_path import get_dataset_path
from splearn import Spectral
from splearn.automaton import Automaton
train_file = '3.pautomac_light.train'
data = load_data_sample(adr=get_dataset_path(train_file))
sp = Spectral()
sp.fit(X=data.data)
sp.automaton.write_json("test.json")
A = Automaton(1,1,[]).read_json("test.json")
print(A)
{"automaton": {"final": {"numpy.ndarray": {"values": [0.15, 0.9], "dtype": "float64"}}, "initial": {"numpy.ndarray": {"values": [0.5, 0.8], "dtype": "float64"}}, "transitions": [{"numpy.ndarray": {"values": [[0.5, 0.15], [0.9, 0]], "dtype": "float64"}}, {"numpy.ndarray": {"values": [[0, 0], [0, -0.15]], "dtype": "float64"}}], "type": "classic", "nbS": 2, "nbL": 2}}
//Simple Automata
digraph {
0 [label="0
______
> 0.50
0.15 >"]
1 [label="1
______
> 0.80
0.90 >"]
0 -> 0 [label="0:0.50"]
0 -> 1 [label="0:0.15"]
1 -> 0 [label="0:0.90"]
1 -> 1 [label="1:-0.15"]
}
File added
automaton:
final:
numpy.ndarray:
dtype: float64
values: [0.15, 0.9]
initial:
numpy.ndarray:
dtype: float64
values: [0.5, 0.8]
nbL: 2
nbS: 2
transitions:
- numpy.ndarray:
dtype: float64
values:
- [0.5, 0.15]
- [0.9, 0]
- numpy.ndarray:
dtype: float64
values:
- [0, 0]
- [0, -0.15]
type: classic
//Simple Automata
digraph {
0 [label="0
______
> 0.50
0.15 >"]
1 [label="1
______
> 0.80
0.90 >"]
0 -> 0 [label="0:0.50"]
0 -> 1 [label="0:0.15"]
1 -> 0 [label="0:0.90"]
1 -> 1 [label="1:-0.15"]
}
File added
{"nbL": 4, "nbS": 5, "initial": {"py/numpy.ndarray": {"values": [-0.0004934419970497512, 0.0030634697107912346, -0.044073932015580415, -0.1077770261654714, -0.0866391379316952], "dtype": "float64"}}, "final": {"py/numpy.ndarray": {"values": [0.07757136847945045, -0.024220294003132026, -0.4468125366321221, 0.627732084089759, -0.554674433356224], "dtype": "float64"}}, "transitions": [{"py/numpy.ndarray": {"values": [[0.04512120959511772, -0.24038969827844062, 0.34944999592135334, -0.2811680730534579, -0.21402523377497645], [0.0692580056243761, -0.30062293462829204, 0.20641375368520157, -0.14960814319756124, -0.5580573163749153], [0.02980115192176571, -0.13866480809160409, 0.18362212572805459, -0.20969545230657607, -0.14481622025561292], [0.005699344003198349, -0.023385825120201414, -0.06600665373981851, 0.10749935271466007, -0.15103654604159977], [-0.02008655193147911, 0.09026347555230492, -0.005525585655539262, -0.031355317090308935, 0.2432902242047721]], "dtype": "float64"}}, {"py/numpy.ndarray": {"values": [[0.0774477207917058, 0.09007073705762021, -0.3047220063293013, 0.2767624549859105, 0.20289396030628148], [-0.09902980483670844, -0.08061846818727973, 0.25853170692250554, -0.12086330214608881, -0.11085207725068251], [-0.061710792028537534, -0.06244151779954751, 0.12007654564862075, 0.0025063746277943564, -0.1567967473145572], [-0.002736973749965403, -0.009005721984277787, -0.00046003295909181354, -0.008550426472005344, -0.053754646789681754], [0.030987327588710728, 0.03972680066723246, -0.04997113350910248, 0.0035769411874962344, 0.1418257620585633]], "dtype": "float64"}}, {"py/numpy.ndarray": {"values": [[-0.06791915236220235, -0.11357937659088102, 0.37955392604054394, -0.21784979894046635, -0.22977695089938127], [0.11596642335411328, 0.14914956804629287, -0.13357508376686902, -0.008916063072034974, 0.3484153673774836], [0.011730817547426673, 0.019273800531955612, 0.0414265834586712, -0.035346588560982, 0.02316491010895583], [0.007328911075541707, 0.005536509132796312, -0.022456082950666856, 0.03611543477693187, -0.038514339001406585], [-0.010589894686551544, -0.010626616553723532, -0.000543105645661794, -0.025567476700160314, 0.04984888818929034]], "dtype": "float64"}}, {"py/numpy.ndarray": {"values": [[0.07276211427780357, -0.0157195576855797, 0.07428592814590385, -0.10369861539249735, 0.024753473688328077], [-0.05607105449779142, -0.08896207276035666, 0.27638225397521243, -0.2371125582838589, 0.07372294122306285], [-0.007391294007753122, -0.048741797963875705, -0.6291239733858526, 0.46816276521577677, 0.09251699239093385], [-0.007110224931878467, -0.05623317735898056, -0.36606658567620365, -0.013297798115225407, 0.6491033177492604], [0.002335515008556511, -0.021561151264484414, 0.09096243479437888, -0.38438823493062646, 0.6616477207948602]], "dtype": "float64"}}], "type": "classic"}
\ No newline at end of file
from splearn.automaton import Automaton
#from splearn.spectral import Learning
from splearn.serializer import Serializer
from splearn.spectral import Spectral
from splearn.hankel import Hankel
__version__ = "1.1.0"
......@@ -3,7 +3,7 @@
"""
import numpy as np
from splearn.serializer import Serializer
class Automaton(object):
""" Define an automaton with parameters
......@@ -689,24 +689,47 @@ class Automaton(object):
out += "}\n"
return out
def write_json(self, jsonFileName):
data = {"nbL":self.nbL, "nbS":self.nbS, "initial":self.initial, "final":self.final,
"transitions":self.transitions, "type":self.type}
data_str = Serializer().data_to_json(data)
with open(jsonFileName, 'w') as outfile:
@staticmethod
def write(automaton_in, filename, format='json'):
""" write input automaton into a file with the given format.
- Input:
:param Automaton automaton_in: automaton to write into the file
:param str filename: the name of the file. If it does not exist,
the file is created.
:param str format: 'json' or yaml'
"""
from splearn.serializer import Serializer
if format == 'json':
data_str = Serializer.data_to_json(automaton_in)
elif format == 'yaml':
data_str = Serializer.data_to_yaml(automaton_in)
else:
raise ValueError("Invalid input format. Should be \"json\" or \"yaml\"")
with open(filename, 'w') as outfile:
outfile.write(data_str)
def read_json(self, jsonFileName):
with open(jsonFileName) as infile:
datastr = infile.read()
data = Serializer().json_to_data(datastr)
print(data)
keys = {"nbL", "nbS", "initial", "final", "transitions", "type"}
if not keys.issubset(set(data.keys())):
raise ValueError("The input json file should contain the following keys : \"" + '\", \"'.join(keys) + "\"")
self = Automaton(nbL=data["nbL"], nbS=data["nbS"], initial=data["initial"], final=data["final"],
transitions=data["transitions"], type=data["type"])
@staticmethod
def read(filename, format='json'):
""" return an Automaton build with attributes read from a file
- Input:
:param str filename: the name of the input file.
:param str format: 'json' or yaml'
- Output:
:returns: the output automaton
:rtype: Automaton
"""
from splearn.serializer import Serializer
with open(filename) as infile:
datastr = infile.read()
if format == 'json':
return Serializer.json_to_data(datastr)
if format == 'yaml':
return Serializer.yaml_to_data(datastr)
raise ValueError("Invalid input format. Should be \"json\" or \"yaml\"")
\ No newline at end of file
'''
Created on 6 févr. 2018
@author: arrivault
'''
# -*- coding: utf-8 -*-
"""This module contains the Serializer class
"""
import numpy as np
import json
from splearn.automaton import Automaton
class Serializer(object):
'''
classdocs
'''
""" Serializer is an helping object for data serialization
"""
def __serialize(self, data):
@staticmethod
def __serialize(data):
if data is None or isinstance(data, (bool, int, float, str)):
return data
if isinstance(data, list):
return [self.__serialize(val) for val in data]
return [Serializer.__serialize(val) for val in data]
if isinstance(data, dict):
if all(isinstance(k, str) for k in data):
return {k: self.__serialize(v) for k, v in data.items()}
return {"py/dict": [[self.__serialize(k), self.__serialize(v)] for k, v in data.items()]}
return {k: Serializer.__serialize(v) for k, v in data.items()}
return {"dict": [[Serializer.__serialize(k), Serializer.__serialize(v)] for k, v in data.items()]}
if isinstance(data, tuple):
return {"py/tuple": [self.__serialize(val) for val in data]}
return {"tuple": [Serializer.__serialize(val) for val in data]}
if isinstance(data, set):
return {"py/set": [self.__serialize(val) for val in data]}
return {"set": [Serializer.__serialize(val) for val in data]}
if isinstance(data, np.ndarray):
return {"py/numpy.ndarray": {
return {"numpy.ndarray": {
"values": data.tolist(),
"dtype": str(data.dtype)}}
raise TypeError("Type %s not data-serializable" % type(data))
def __restore(self, dct):
if "py/dict" in dct:
return dict(dct["py/dict"])
if "py/tuple" in dct:
return tuple(dct["py/tuple"])
if "py/set" in dct:
return set(dct["py/set"])
if "py/numpy.ndarray" in dct:
data = dct["py/numpy.ndarray"]
if isinstance(data, Automaton):
data_dict = {"nbL":data.nbL, "nbS":data.nbS, "initial":data.initial, "final":data.final,
"transitions":data.transitions, "type":data.type}
return {"automaton" : Serializer.__serialize(data_dict)}
raise TypeError("Type %s is not serializabled" % type(data))
@staticmethod
def __restore_json(data_str):
if "dict" in data_str:
return dict(data_str["dict"])
if "tuple" in data_str:
return tuple(data_str["tuple"])
if "set" in data_str:
return set(data_str["set"])
if "numpy.ndarray" in data_str:
data = data_str["numpy.ndarray"]
keys = {"values", "dtype"}
if not keys.issubset(set(data.keys())):
raise ValueError("The input data string (" + data_str +
") should contain the following keys : \"" +
'\", \"'.join(keys) + "\"")
return np.array(data["values"], dtype=data["dtype"])
return dct
if "automaton" in data_str:
data = Serializer.__restore_json(data_str["automaton"])
keys = {"nbL", "nbS", "initial", "final", "transitions", "type"}
if not keys.issubset(set(data.keys())):
raise ValueError("The input data string (" + data_str +
") should contain the following keys : \"" +
'\", \"'.join(keys) + "\"")
return Automaton(nbL=data["nbL"], nbS=data["nbS"], initial=data["initial"], final=data["final"],
transitions=data["transitions"], type=data["type"])
return data_str
@staticmethod
def __restore_yaml(data_str):
if "dict" in data_str:
return dict(data_str["dict"])
if "tuple" in data_str:
return tuple(data_str["tuple"])
if "set" in data_str:
return set(data_str["set"])
if "numpy.ndarray" in data_str:
data = data_str["numpy.ndarray"]
keys = {"values", "dtype"}
if not keys.issubset(set(data.keys())):
raise ValueError("The input data string (" + data_str +
") should contain the following keys : \"" +
'\", \"'.join(keys) + "\"")
return np.array(data["values"], dtype=data["dtype"])
if "automaton" in data_str:
data = Serializer.__restore_yaml(data_str["automaton"])
keys = {"nbL", "nbS", "initial", "final", "transitions", "type"}
if not keys.issubset(set(data.keys())):
raise ValueError("The input data string (" + data_str +
") should contain the following keys : \"" +
'\", \"'.join(keys) + "\"")
return Automaton(nbL=data["nbL"], nbS=data["nbS"], initial=Serializer.__restore_yaml(data["initial"]),
final=Serializer.__restore_yaml(data["final"]),
transitions=[Serializer.__restore_yaml(k) for k in data["transitions"]],
type=data["type"])
return data_str
@staticmethod
def data_to_json(data):
""" return a string into json format that does contains the input data.
- Input:
:param data: data composed by any types that is serializabled
- Output:
:returns: the output string
:rtype: str
"""
import json
return json.dumps(Serializer.__serialize(data))
@staticmethod
def json_to_data(json_data_str):
""" return a data from input json string.
- Input:
:param json_data_str: the json input string
- Output:
:returns: the data
:rtype: deduced form the json input string
"""
import json
return json.loads(json_data_str, object_hook=Serializer.__restore_json)
@staticmethod
def data_to_yaml(data):
""" return a string into yaml format that does contains the input data.
- Input:
:param data: data composed by any types that is serializabled
- Output:
:returns: the output string
:rtype: str
"""
import yaml
return yaml.dump(Serializer.__serialize(data))
@staticmethod
def yaml_to_data(yaml_data_str):
""" return a data from input yaml string.
- Input:
:param yaml_data_str: the yaml input string
def data_to_json(self, data):
return json.dumps(self.__serialize(data))
- Output:
def json_to_data(self, s):
return json.loads(s, object_hook=self.__restore)
:returns: the data
:rtype: deduced form the yaml input string
"""
import yaml
return Serializer.__restore_yaml(yaml.load(yaml_data_str))
{"nbL": 4, "nbS": 5, "initial": {"py/numpy.ndarray": {"values": [-0.0004934419970497512, 0.0030634697107912346, -0.044073932015580415, -0.1077770261654714, -0.0866391379316952], "dtype": "float64"}}, "final": {"py/numpy.ndarray": {"values": [0.07757136847945045, -0.024220294003132026, -0.4468125366321221, 0.627732084089759, -0.554674433356224], "dtype": "float64"}}, "transitions": [{"py/numpy.ndarray": {"values": [[0.04512120959511772, -0.24038969827844062, 0.34944999592135334, -0.2811680730534579, -0.21402523377497645], [0.0692580056243761, -0.30062293462829204, 0.20641375368520157, -0.14960814319756124, -0.5580573163749153], [0.02980115192176571, -0.13866480809160409, 0.18362212572805459, -0.20969545230657607, -0.14481622025561292], [0.005699344003198349, -0.023385825120201414, -0.06600665373981851, 0.10749935271466007, -0.15103654604159977], [-0.02008655193147911, 0.09026347555230492, -0.005525585655539262, -0.031355317090308935, 0.2432902242047721]], "dtype": "float64"}}, {"py/numpy.ndarray": {"values": [[0.0774477207917058, 0.09007073705762021, -0.3047220063293013, 0.2767624549859105, 0.20289396030628148], [-0.09902980483670844, -0.08061846818727973, 0.25853170692250554, -0.12086330214608881, -0.11085207725068251], [-0.061710792028537534, -0.06244151779954751, 0.12007654564862075, 0.0025063746277943564, -0.1567967473145572], [-0.002736973749965403, -0.009005721984277787, -0.00046003295909181354, -0.008550426472005344, -0.053754646789681754], [0.030987327588710728, 0.03972680066723246, -0.04997113350910248, 0.0035769411874962344, 0.1418257620585633]], "dtype": "float64"}}, {"py/numpy.ndarray": {"values": [[-0.06791915236220235, -0.11357937659088102, 0.37955392604054394, -0.21784979894046635, -0.22977695089938127], [0.11596642335411328, 0.14914956804629287, -0.13357508376686902, -0.008916063072034974, 0.3484153673774836], [0.011730817547426673, 0.019273800531955612, 0.0414265834586712, -0.035346588560982, 0.02316491010895583], [0.007328911075541707, 0.005536509132796312, -0.022456082950666856, 0.03611543477693187, -0.038514339001406585], [-0.010589894686551544, -0.010626616553723532, -0.000543105645661794, -0.025567476700160314, 0.04984888818929034]], "dtype": "float64"}}, {"py/numpy.ndarray": {"values": [[0.07276211427780357, -0.0157195576855797, 0.07428592814590385, -0.10369861539249735, 0.024753473688328077], [-0.05607105449779142, -0.08896207276035666, 0.27638225397521243, -0.2371125582838589, 0.07372294122306285], [-0.007391294007753122, -0.048741797963875705, -0.6291239733858526, 0.46816276521577677, 0.09251699239093385], [-0.007110224931878467, -0.05623317735898056, -0.36606658567620365, -0.013297798115225407, 0.6491033177492604], [0.002335515008556511, -0.021561151264484414, 0.09096243479437888, -0.38438823493062646, 0.6616477207948602]], "dtype": "float64"}}], "type": "classic"}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment