From 6db76b1496d3ab755acd3c8bd3228255685d4a80 Mon Sep 17 00:00:00 2001 From: FredB-mine Date: Thu, 7 Mar 2024 20:05:23 +0800 Subject: [PATCH 1/4] Formatted code --- cyaron/compare.py | 135 +++++++++--- cyaron/consts.py | 4 +- cyaron/graders/fulltext.py | 12 +- cyaron/graders/graderregistry.py | 2 +- cyaron/graders/mismatch.py | 25 ++- cyaron/graders/noipstyle.py | 44 ++-- cyaron/graph.py | 312 +++++++++++++++------------- cyaron/io.py | 124 ++++++----- cyaron/log.py | 82 +++++--- cyaron/math.py | 342 +++++++++++++++++++------------ cyaron/merger.py | 52 ++--- cyaron/output_capture.py | 4 +- cyaron/polygon.py | 73 ++++--- cyaron/sequence.py | 10 +- cyaron/string.py | 13 +- cyaron/tests/compare_test.py | 76 ++++--- cyaron/tests/graph_test.py | 72 ++++--- cyaron/tests/io_test.py | 30 ++- cyaron/tests/polygon_test.py | 20 +- cyaron/tests/sequence_test.py | 1 - cyaron/tests/str_test.py | 4 +- cyaron/utils.py | 21 +- cyaron/vector.py | 22 +- cyaron/visual.py | 24 +-- 24 files changed, 931 insertions(+), 573 deletions(-) diff --git a/cyaron/compare.py b/cyaron/compare.py index ad613a0..66f76a7 100644 --- a/cyaron/compare.py +++ b/cyaron/compare.py @@ -17,7 +17,7 @@ def __init__(self, name, mismatch): self.mismatch = mismatch def __str__(self): - return 'In program: \'{}\'. {}'.format(self.name,self.mismatch) + return "In program: '{}'. {}".format(self.name, self.mismatch) class Compare: @@ -37,7 +37,7 @@ def __process_file(file): file.output_file.seek(0) return file.output_filename, file.output_file.read() else: - with open(file, "r", newline='\n') as f: + with open(file, "r", newline="\n") as f: return file, f.read() @staticmethod @@ -50,26 +50,43 @@ def __normal_max_workers(workers): @classmethod def output(cls, *files, **kwargs): - kwargs = unpack_kwargs('output', kwargs, ('std', ('grader', DEFAULT_GRADER), ('max_workers', -1), - ('job_pool', None), ('stop_on_incorrect', None))) - std = kwargs['std'] - grader = kwargs['grader'] - max_workers = kwargs['max_workers'] - job_pool = kwargs['job_pool'] - if kwargs['stop_on_incorrect'] is not None: + kwargs = unpack_kwargs( + "output", + kwargs, + ( + "std", + ("grader", DEFAULT_GRADER), + ("max_workers", -1), + ("job_pool", None), + ("stop_on_incorrect", None), + ), + ) + std = kwargs["std"] + grader = kwargs["grader"] + max_workers = kwargs["max_workers"] + job_pool = kwargs["job_pool"] + if kwargs["stop_on_incorrect"] is not None: log.warn("parameter stop_on_incorrect is deprecated and has no effect.") if (max_workers is None or max_workers >= 0) and job_pool is None: max_workers = cls.__normal_max_workers(max_workers) try: from concurrent.futures import ThreadPoolExecutor + with ThreadPoolExecutor(max_workers=max_workers) as job_pool: - return cls.output(*files, std=std, grader=grader, max_workers=max_workers, job_pool=job_pool) + return cls.output( + *files, + std=std, + grader=grader, + max_workers=max_workers, + job_pool=job_pool + ) except ImportError: pass def get_std(): return cls.__process_file(std)[1] + if job_pool is not None: std = job_pool.submit(get_std).result() else: @@ -86,61 +103,121 @@ def do(file): @classmethod def program(cls, *programs, **kwargs): - kwargs = unpack_kwargs('program', kwargs, ('input', ('std', None), ('std_program', None), - ('grader', DEFAULT_GRADER), ('max_workers', -1), - ('job_pool', None), ('stop_on_incorrect', None))) - input = kwargs['input'] - std = kwargs['std'] - std_program = kwargs['std_program'] - grader = kwargs['grader'] - max_workers = kwargs['max_workers'] - job_pool = kwargs['job_pool'] - if kwargs['stop_on_incorrect'] is not None: + kwargs = unpack_kwargs( + "program", + kwargs, + ( + "input", + ("std", None), + ("std_program", None), + ("grader", DEFAULT_GRADER), + ("max_workers", -1), + ("job_pool", None), + ("stop_on_incorrect", None), + ), + ) + input = kwargs["input"] + std = kwargs["std"] + std_program = kwargs["std_program"] + grader = kwargs["grader"] + max_workers = kwargs["max_workers"] + job_pool = kwargs["job_pool"] + time_limit = kwargs["time_limit"] + memory_limit = kwargs["memory_limit"] + + if kwargs["stop_on_incorrect"] is not None: log.warn("parameter stop_on_incorrect is deprecated and has no effect.") if (max_workers is None or max_workers >= 0) and job_pool is None: max_workers = cls.__normal_max_workers(max_workers) try: from concurrent.futures import ThreadPoolExecutor + with ThreadPoolExecutor(max_workers=max_workers) as job_pool: - return cls.program(*programs, input=input, std=std, std_program=std_program, grader=grader, max_workers=max_workers, job_pool=job_pool) + return cls.program( + *programs, + input=input, + std=std, + std_program=std_program, + grader=grader, + max_workers=max_workers, + job_pool=job_pool + ) except ImportError: pass if not isinstance(input, IO): - raise TypeError("expect {}, got {}".format(type(IO).__name__, type(input).__name__)) + raise TypeError( + "expect {}, got {}".format(type(IO).__name__, type(input).__name__) + ) input.flush_buffer() input.input_file.seek(0) if std_program is not None: + def get_std(): - with open(os.dup(input.input_file.fileno()), 'r', newline='\n') as input_file: - content = make_unicode(subprocess.check_output(std_program, shell=(not list_like(std_program)), stdin=input.input_file, universal_newlines=True)) + with open( + os.dup(input.input_file.fileno()), "r", newline="\n" + ) as input_file: + content = make_unicode( + subprocess.check_output( + std_program, + shell=(not list_like(std_program)), + stdin=input.input_file, + universal_newlines=True, + ) + ) input_file.seek(0) return content + if job_pool is not None: std = job_pool.submit(get_std).result() else: std = get_std() elif std is not None: + def get_std(): return cls.__process_file(std)[1] + if job_pool is not None: std = job_pool.submit(get_std).result() else: std = get_std() else: - raise TypeError('program() missing 1 required non-None keyword-only argument: \'std\' or \'std_program\'') + raise TypeError( + "program() missing 1 required non-None keyword-only argument: 'std' or 'std_program'" + ) def do(program_name): timeout = None - if list_like(program_name) and len(program_name) == 2 and int_like(program_name[-1]): + if ( + list_like(program_name) + and len(program_name) == 2 + and int_like(program_name[-1]) + ): program_name, timeout = program_name - with open(os.dup(input.input_file.fileno()), 'r', newline='\n') as input_file: + with open( + os.dup(input.input_file.fileno()), "r", newline="\n" + ) as input_file: if timeout is None: - content = make_unicode(subprocess.check_output(program_name, shell=(not list_like(program_name)), stdin=input_file, universal_newlines=True)) + content = make_unicode( + subprocess.check_output( + program_name, + shell=(not list_like(program_name)), + stdin=input_file, + universal_newlines=True, + ) + ) else: - content = make_unicode(subprocess.check_output(program_name, shell=(not list_like(program_name)), stdin=input_file, universal_newlines=True, timeout=timeout)) + content = make_unicode( + subprocess.check_output( + program_name, + shell=(not list_like(program_name)), + stdin=input_file, + universal_newlines=True, + timeout=timeout, + ) + ) input_file.seek(0) cls.__compare_two(program_name, content, std, grader) diff --git a/cyaron/consts.py b/cyaron/consts.py index c838ef3..e6fab00 100644 --- a/cyaron/consts.py +++ b/cyaron/consts.py @@ -18,7 +18,7 @@ ALPHABET_CAPITAL = string.ascii_uppercase ALPHABET = ALPHABET_SMALL + ALPHABET_CAPITAL NUMBERS = string.digits -SENTENCE_SEPARATORS = ',,,,,,,;;:' # 70% ',' 20% ';' 10% ':' -SENTENCE_TERMINATORS = '....!' # 80% '.' 20% '!' +SENTENCE_SEPARATORS = ",,,,,,,;;:" # 70% ',' 20% ';' 10% ':' +SENTENCE_TERMINATORS = "....!" # 80% '.' 20% '!' DEFAULT_GRADER = "NOIPStyle" diff --git a/cyaron/graders/fulltext.py b/cyaron/graders/fulltext.py index 8460b6f..b5dbb07 100644 --- a/cyaron/graders/fulltext.py +++ b/cyaron/graders/fulltext.py @@ -2,9 +2,13 @@ from .graderregistry import CYaRonGraders from .mismatch import HashMismatch + @CYaRonGraders.grader("FullText") def fulltext(content, std): - content_hash = hashlib.sha256(content.encode('utf-8')).hexdigest() - std_hash = hashlib.sha256(std.encode('utf-8')).hexdigest() - return (True, None) if content_hash == std_hash else (False, HashMismatch(content, std, content_hash, std_hash)) - + content_hash = hashlib.sha256(content.encode("utf-8")).hexdigest() + std_hash = hashlib.sha256(std.encode("utf-8")).hexdigest() + return ( + (True, None) + if content_hash == std_hash + else (False, HashMismatch(content, std, content_hash, std_hash)) + ) diff --git a/cyaron/graders/graderregistry.py b/cyaron/graders/graderregistry.py index 2fd1419..702e39d 100644 --- a/cyaron/graders/graderregistry.py +++ b/cyaron/graders/graderregistry.py @@ -15,4 +15,4 @@ def check(self, name): return name in self._registry -CYaRonGraders = GraderRegistry() \ No newline at end of file +CYaRonGraders = GraderRegistry() diff --git a/cyaron/graders/mismatch.py b/cyaron/graders/mismatch.py index 70c2dfc..7c8eb21 100644 --- a/cyaron/graders/mismatch.py +++ b/cyaron/graders/mismatch.py @@ -1,5 +1,6 @@ class Mismatch(ValueError): """exception for content mismatch""" + def __init__(self, content, std, *args): """ content -> content got @@ -9,8 +10,10 @@ def __init__(self, content, std, *args): self.content = content self.std = std + class HashMismatch(Mismatch): """exception for hash mismatch""" + def __init__(self, content, std, content_hash, std_hash): """ content -> content got @@ -23,11 +26,25 @@ def __init__(self, content, std, content_hash, std_hash): self.std_hash = std_hash def __str__(self): - return "Hash mismatch: read %s, expected %s" % (self.content_hash, self.std_hash) + return "Hash mismatch: read %s, expected %s" % ( + self.content_hash, + self.std_hash, + ) + class TextMismatch(Mismatch): """exception for text mismatch""" - def __init__(self, content, std, err_msg, lineno=None, colno=None, content_token=None, std_token=None): + + def __init__( + self, + content, + std, + err_msg, + lineno=None, + colno=None, + content_token=None, + std_token=None, + ): """ content -> content got std -> content expected @@ -37,7 +54,9 @@ def __init__(self, content, std, err_msg, lineno=None, colno=None, content_token content_token -> the token of content mismatch std_token -> the token of std """ - super(TextMismatch, self).__init__(content, std, err_msg, lineno, colno, content_token, std_token) + super(TextMismatch, self).__init__( + content, std, err_msg, lineno, colno, content_token, std_token + ) self.err_msg = err_msg.format(lineno, colno, content_token, std_token) self.lineno = lineno self.colno = colno diff --git a/cyaron/graders/noipstyle.py b/cyaron/graders/noipstyle.py index bf6fa21..679e059 100644 --- a/cyaron/graders/noipstyle.py +++ b/cyaron/graders/noipstyle.py @@ -5,28 +5,46 @@ @CYaRonGraders.grader("NOIPStyle") def noipstyle(content, std): - content_lines = strtolines(content.replace('\r\n', '\n')) - std_lines = strtolines(std.replace('\r\n', '\n')) + content_lines = strtolines(content.replace("\r\n", "\n")) + std_lines = strtolines(std.replace("\r\n", "\n")) if len(content_lines) != len(std_lines): - return False, TextMismatch(content, std, 'Too many or too few lines.') + return False, TextMismatch(content, std, "Too many or too few lines.") for i in range(len(content_lines)): if std_lines[i] != content_lines[i]: for j in range(min(len(std_lines[i]), len(content_lines[i]))): if std_lines[i][j] != content_lines[i][j]: - return (False, - TextMismatch( - content, std, - 'On line {} column {}, read {}, expected {}.', - i + 1, j + 1, content_lines[i][j:j + 5], - std_lines[i][j:j + 5])) + return ( + False, + TextMismatch( + content, + std, + "On line {} column {}, read {}, expected {}.", + i + 1, + j + 1, + content_lines[i][j : j + 5], + std_lines[i][j : j + 5], + ), + ) if len(std_lines[i]) > len(content_lines[i]): return False, TextMismatch( - content, std, 'Too short on line {}.', i + 1, j + 1, - content_lines[i][j:j + 5], std_lines[i][j:j + 5]) + content, + std, + "Too short on line {}.", + i + 1, + j + 1, + content_lines[i][j : j + 5], + std_lines[i][j : j + 5], + ) if len(std_lines[i]) < len(content_lines[i]): return False, TextMismatch( - content, std, 'Too long on line {}.', i + 1, j + 1, - content_lines[i][j:j + 5], std_lines[i][j:j + 5]) + content, + std, + "Too long on line {}.", + i + 1, + j + 1, + content_lines[i][j : j + 5], + std_lines[i][j : j + 5], + ) return True, None diff --git a/cyaron/graph.py b/cyaron/graph.py index 34add04..c95e8b5 100644 --- a/cyaron/graph.py +++ b/cyaron/graph.py @@ -4,12 +4,13 @@ class Edge: """Class Edge: A class of the edge in the graph""" + def __init__(self, u, v, w): """__init__(self, u, v, w) -> None - Initialize a edge. - int u -> the start vertex - int v -> the end vertex - int w -> the weight. + Initialize a edge. + int u -> the start vertex + int v -> the end vertex + int w -> the weight. """ self.start = u self.end = v @@ -17,35 +18,36 @@ def __init__(self, u, v, w): def __str__(self): """__str__(self) -> str - Return a string to output the edge. The string contains the start vertex, end vertex and weight(u,v,w) and splits with space. + Return a string to output the edge. The string contains the start vertex, end vertex and weight(u,v,w) and splits with space. """ return "%d %d %d" % (self.start, self.end, self.weight) @staticmethod def unweighted_edge(edge): """unweighted_edge(edge) -> str - Return a string to output the edge without weight. The string contains the start vertex, end vertex(u,v) and splits with space. + Return a string to output the edge without weight. The string contains the start vertex, end vertex(u,v) and splits with space. """ - return '%d %d'%(edge.start,edge.end) + return "%d %d" % (edge.start, edge.end) + class Graph: - """Class Graph: A class of the graph - """ + """Class Graph: A class of the graph""" + def __init__(self, point_count, directed=False): """__init__(self, point_count) -> None - Initialize a graph. - int point_count -> the count of the vertexes in the graph. - bool directed = False -> whether the graph is directed(true:directed,false:not directed) + Initialize a graph. + int point_count -> the count of the vertexes in the graph. + bool directed = False -> whether the graph is directed(true:directed,false:not directed) """ self.directed = directed self.edges = [[] for i in range(point_count + 1)] def to_str(self, **kwargs): """to_str(self, **kwargs) -> str - Convert the graph to string with format. Splits with "\n" - **kwargs(Keyword args): - bool shuffle = False -> whether shuffle the output or not - str output(Edge) = str -> the convert function which converts object Edge to str. the default way is to use str() + Convert the graph to string with format. Splits with "\n" + **kwargs(Keyword args): + bool shuffle = False -> whether shuffle the output or not + str output(Edge) = str -> the convert function which converts object Edge to str. the default way is to use str() """ shuffle = kwargs.get("shuffle", False) output = kwargs.get("output", str) @@ -57,7 +59,8 @@ def to_str(self, **kwargs): edge_buf = [] for edge in self.iterate_edges(): edge_buf.append( - Edge(new_node_id[edge.start], new_node_id[edge.end], edge.weight)) + Edge(new_node_id[edge.start], new_node_id[edge.end], edge.weight) + ) random.shuffle(edge_buf) for edge in edge_buf: if not self.directed and random.randint(0, 1) == 0: @@ -70,13 +73,13 @@ def to_str(self, **kwargs): def __str__(self): """__str__(self) -> str - Return a string to output the graph. The string contains all the edges of the graph, splits with "\n". + Return a string to output the graph. The string contains all the edges of the graph, splits with "\n". """ return self.to_str() def iterate_edges(self): """iterate_edges(self) -> Edge - Iter the graph. Order by the start vertex. + Iter the graph. Order by the start vertex. """ for node in self.edges: for edge in node: @@ -85,16 +88,16 @@ def iterate_edges(self): def __add_edge(self, x, y, w): """__add_edge(self, x, y, w) -> None - Add an edge to the graph. + Add an edge to the graph. """ self.edges[x].append(Edge(x, y, w)) def add_edge(self, x, y, **kwargs): """add_edge(self, x, y, **kwargs) -> None - int x -> the start vertex - int y -> the end vertex - **kwargs(Keyword args): - int weight = 1 -> the weight + int x -> the start vertex + int y -> the end vertex + **kwargs(Keyword args): + int weight = 1 -> the weight """ weight = kwargs.get("weight", 1) self.__add_edge(x, y, weight) @@ -104,56 +107,56 @@ def add_edge(self, x, y, **kwargs): @staticmethod def chain(point_count, **kwargs): """chain(point_count, **kwargs) -> Graph - Factory method. Return a chain graph with point_count vertexes. - int point_count -> the count of vertexes - **kwargs(Keyword args): - bool directed = True -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a chain graph with point_count vertexes. + int point_count -> the count of vertexes + **kwargs(Keyword args): + bool directed = True -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ return Graph.tree(point_count, 1, 0, **kwargs) @staticmethod def flower(point_count, **kwargs): """flower(point_count, **kwargs) -> Graph - Factory method. Return a flower graph with point_count vertexes. - int point_count -> the count of vertexes - **kwargs(Keyword args): - bool directed = True -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a flower graph with point_count vertexes. + int point_count -> the count of vertexes + **kwargs(Keyword args): + bool directed = True -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ return Graph.tree(point_count, 0, 1, **kwargs) @staticmethod def tree(point_count, chain=0, flower=0, **kwargs): """tree(point_count, chain=0, flower=0, **kwargs) -> Graph - Factory method. Return a tree with point_count vertexes. - int point_count -> the count of vertexes - float chain = 0 -> 1 means the tree is a chain - float flower = 0 -> 1 means the tree is a flower - NOTICE:only either chain or flower can be True - **kwargs(Keyword args): - bool directed = False -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a tree with point_count vertexes. + int point_count -> the count of vertexes + float chain = 0 -> 1 means the tree is a chain + float flower = 0 -> 1 means the tree is a flower + NOTICE:only either chain or flower can be True + **kwargs(Keyword args): + bool directed = False -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ directed = kwargs.get("directed", False) weight_limit = kwargs.get("weight_limit", (1, 1)) if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint( - weight_limit[0], weight_limit[1])) + "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) + ) if not 0 <= chain <= 1 or not 0 <= flower <= 1: raise Exception("chain and flower must be between 0 and 1") @@ -182,51 +185,51 @@ def tree(point_count, chain=0, flower=0, **kwargs): @staticmethod def binary_tree(point_count, left=0, right=0, **kwargs): """binary_tree(point_count, left=0, right=0, **kwargs) -> Graph - Factory method. Return a binary tree with point_count vertexes. - int point_count -> the count of vertexes - float left = 0 -> random arg. should be in [0,1] - float right = 0 -> random arg. should be in [0,1] - NOTICE:left+right mustn't be greater than 1 - **kwargs(Keyword args): - bool directed = False -> whether the binary tree is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a binary tree with point_count vertexes. + int point_count -> the count of vertexes + float left = 0 -> random arg. should be in [0,1] + float right = 0 -> random arg. should be in [0,1] + NOTICE:left+right mustn't be greater than 1 + **kwargs(Keyword args): + bool directed = False -> whether the binary tree is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ directed = kwargs.get("directed", False) weight_limit = kwargs.get("weight_limit", (1, 1)) if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint( - weight_limit[0], weight_limit[1])) + "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) + ) if not 0 <= left <= 1 or not 0 <= right <= 1: raise Exception("left and right must be between 0 and 1") if left + right > 1: raise Exception("left plus right must be smaller than 1") - - can_left=[1] - can_right=[1] + + can_left = [1] + can_right = [1] graph = Graph(point_count, directed) for i in range(2, point_count + 1): edge_pos = random.random() node = 0 # Left if edge_pos < left or left + right < edge_pos <= (1.0 - left - right) / 2: - point_index = random.randint(0,len(can_left)-1) + point_index = random.randint(0, len(can_left) - 1) node = can_left[point_index] - del_last_node = can_left.pop() # Save a copy of the last element + del_last_node = can_left.pop() # Save a copy of the last element if point_index < len(can_left): # If the chosen element isn't the last one, # Copy the last one to the position of the chosen one can_left[point_index] = del_last_node # Right else: - # elif left <= edge_pos <= left + right or (1.0 - left - right) / 2 < edge_pos < 1: - point_index = random.randint(0,len(can_right)-1) + # elif left <= edge_pos <= left + right or (1.0 - left - right) / 2 < edge_pos < 1: + point_index = random.randint(0, len(can_right) - 1) node = can_right[point_index] del_last_node = can_right.pop() if point_index < len(can_right): @@ -240,18 +243,18 @@ def binary_tree(point_count, left=0, right=0, **kwargs): @staticmethod def graph(point_count, edge_count, **kwargs): """graph(point_count, edge_count, **kwargs) -> Graph - Factory method. Return a graph with point_count vertexes and edge_count edges. - int point_count -> the count of vertexes - int edge_count -> the count of edges - **kwargs(Keyword args): - bool self_loop = True -> whether to allow self loops or not - bool repeated_edges = True -> whether to allow repeated edges or not - bool directed = False -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a graph with point_count vertexes and edge_count edges. + int point_count -> the count of vertexes + int edge_count -> the count of edges + **kwargs(Keyword args): + bool self_loop = True -> whether to allow self loops or not + bool repeated_edges = True -> whether to allow repeated edges or not + bool directed = False -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ directed = kwargs.get("directed", False) self_loop = kwargs.get("self_loop", True) @@ -260,8 +263,8 @@ def graph(point_count, edge_count, **kwargs): if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint( - weight_limit[0], weight_limit[1])) + "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) + ) graph = Graph(point_count, directed) used_edges = set() i = 0 @@ -269,7 +272,9 @@ def graph(point_count, edge_count, **kwargs): u = random.randint(1, point_count) v = random.randint(1, point_count) - if (not self_loop and u == v) or (not repeated_edges and (u, v) in used_edges): + if (not self_loop and u == v) or ( + not repeated_edges and (u, v) in used_edges + ): # Then we generate a new pair of nodes continue @@ -286,32 +291,34 @@ def graph(point_count, edge_count, **kwargs): @staticmethod def DAG(point_count, edge_count, **kwargs): """DAG(point_count, edge_count, **kwargs) -> Graph - Factory method. Return a graph with point_count vertexes and edge_count edges. - int point_count -> the count of vertexes - int edge_count -> the count of edges - **kwargs(Keyword args): - bool self_loop = False -> whether to allow self loops or not - bool repeated_edges = True -> whether to allow repeated edges or not - bool loop = False -> whether to allow loops or not - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a graph with point_count vertexes and edge_count edges. + int point_count -> the count of vertexes + int edge_count -> the count of edges + **kwargs(Keyword args): + bool self_loop = False -> whether to allow self loops or not + bool repeated_edges = True -> whether to allow repeated edges or not + bool loop = False -> whether to allow loops or not + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ if edge_count < point_count - 1: - raise Exception("the number of edges of connected graph must more than the number of nodes - 1") + raise Exception( + "the number of edges of connected graph must more than the number of nodes - 1" + ) - self_loop = kwargs.get("self_loop", False) # DAG default has no loop + self_loop = kwargs.get("self_loop", False) # DAG default has no loop repeated_edges = kwargs.get("repeated_edges", True) loop = kwargs.get("loop", False) weight_limit = kwargs.get("weight_limit", (1, 1)) if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint( - weight_limit[0], weight_limit[1])) - + "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) + ) + used_edges = set() edge_buf = list(Graph.tree(point_count, weight_gen=weight_gen).iterate_edges()) graph = Graph(point_count, directed=True) @@ -320,10 +327,10 @@ def DAG(point_count, edge_count, **kwargs): if loop and random.randint(1, 2) == 1: edge.start, edge.end = edge.end, edge.start graph.add_edge(edge.start, edge.end, weight=edge.weight) - + if not repeated_edges: used_edges.add((edge.start, edge.end)) - + i = point_count - 1 while i < edge_count: u = random.randint(1, point_count) @@ -332,7 +339,9 @@ def DAG(point_count, edge_count, **kwargs): if not loop and u > v: u, v = v, u - if (not self_loop and u == v) or (not repeated_edges and (u, v) in used_edges): + if (not self_loop and u == v) or ( + not repeated_edges and (u, v) in used_edges + ): # Then we generate a new pair of nodes continue @@ -348,20 +357,22 @@ def DAG(point_count, edge_count, **kwargs): @staticmethod def UDAG(point_count, edge_count, **kwargs): """UDAG(point_count, edge_count, **kwargs) -> Graph - Factory method. Return a graph with point_count vertexes and edge_count edges. - int point_count -> the count of vertexes - int edge_count -> the count of edges - **kwargs(Keyword args): - bool self_loop = True -> whether to allow self loops or not - bool repeated_edges = True -> whether to allow repeated edges or not - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a graph with point_count vertexes and edge_count edges. + int point_count -> the count of vertexes + int edge_count -> the count of edges + **kwargs(Keyword args): + bool self_loop = True -> whether to allow self loops or not + bool repeated_edges = True -> whether to allow repeated edges or not + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ - if edge_count < point_count - 1: - raise Exception("the number of edges of connected graph must more than the number of nodes - 1") + if edge_count < point_count - 1: + raise Exception( + "the number of edges of connected graph must more than the number of nodes - 1" + ) self_loop = kwargs.get("self_loop", True) repeated_edges = kwargs.get("repeated_edges", True) @@ -369,9 +380,9 @@ def UDAG(point_count, edge_count, **kwargs): if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint( - weight_limit[0], weight_limit[1])) - + "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) + ) + used_edges = set() graph = Graph.tree(point_count, weight_gen=weight_gen, directed=False) @@ -379,13 +390,15 @@ def UDAG(point_count, edge_count, **kwargs): if not repeated_edges: used_edges.add((edge.start, edge.end)) used_edges.add((edge.end, edge.start)) - + i = point_count - 1 while i < edge_count: u = random.randint(1, point_count) v = random.randint(1, point_count) - if (not self_loop and u == v) or (not repeated_edges and (u, v) in used_edges): + if (not self_loop and u == v) or ( + not repeated_edges and (u, v) in used_edges + ): # Then we generate a new pair of nodes continue @@ -402,16 +415,16 @@ def UDAG(point_count, edge_count, **kwargs): @staticmethod def hack_spfa(point_count, **kwargs): """hack_spfa(point_count, **kwargs) -> None - Factory method. Return a spfa graph with point_count vertexes - int point_count -> the count of vertexes - **kwargs(Keyword args): - bool directed = False -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int extra_edge = 2 -> the number of extra edges - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a spfa graph with point_count vertexes + int point_count -> the count of vertexes + **kwargs(Keyword args): + bool directed = False -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int extra_edge = 2 -> the number of extra edges + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ directed = kwargs.get("directed", False) extraedg = kwargs.get("extra_edge", 2) @@ -419,8 +432,8 @@ def hack_spfa(point_count, **kwargs): if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint( - weight_limit[0], weight_limit[1])) + "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) + ) point_to_skip = point_count + 3 graph = Graph(point_count, directed) @@ -430,15 +443,18 @@ def hack_spfa(point_count, **kwargs): for i in range(1, half): (x, y) = (i, i + 1) - graph.add_edge(x + (x >= point_to_skip), y + - (y >= point_to_skip), weight=weight_gen()) + graph.add_edge( + x + (x >= point_to_skip), y + (y >= point_to_skip), weight=weight_gen() + ) (x, y) = (i + half, i + half + 1) - graph.add_edge(x + (x >= point_to_skip), y + - (y >= point_to_skip), weight=weight_gen()) + graph.add_edge( + x + (x >= point_to_skip), y + (y >= point_to_skip), weight=weight_gen() + ) for i in range(1, half + 1): (x, y) = (i, i + half) - graph.add_edge(x + (x >= point_to_skip), y + - (y >= point_to_skip), weight=weight_gen()) + graph.add_edge( + x + (x >= point_to_skip), y + (y >= point_to_skip), weight=weight_gen() + ) for i in range(extraedg): u = random.randint(1, point_count) diff --git a/cyaron/io.py b/cyaron/io.py index 1880143..2c00ca7 100644 --- a/cyaron/io.py +++ b/cyaron/io.py @@ -10,39 +10,53 @@ class IO(object): """Class IO: IO tool class. It will process the input and output files.""" - def __init__(self, input_file=None, output_file=None, data_id=None, file_prefix=None, input_suffix='.in', output_suffix='.out', disable_output=False): + + def __init__( + self, + input_file=None, + output_file=None, + data_id=None, + file_prefix=None, + input_suffix=".in", + output_suffix=".out", + disable_output=False, + ): """__init__(self, input_file=None, output_file=None, data_id=None, file_prefix=None, input_suffix='.in', output_suffix='.out', disable_output=False) -> None - input_file, output_file overload: - None -> make a temp file (if file_prefix is None) - file object -> treat the file-like object as in/output file - int -> open file by file descriptor - str -> a filename or filename template like 'awd{}.in'. ``{}`` will be replaced by ``data_id`` - int data_id -> the id of the data. if it's None, the file names will not contain the id. - legacy argumants: - str file_prefix -> the prefix for the input and output files - str input_suffix = ".in" -> the suffix of the input file - str output_suffix = ".out" -> the suffix of the output file - disable_output -> bool, set to True to disable output - Examples: - IO("a","b") -> create input file "a" and output file "b" - IO("a.in","b.out") -> create input file "a.in" and output file "b.out" - IO(file_prefix="data") -> create input file "data.in" and output file "data.out" - IO(file_prefix="data",data_id=1) -> create input file "data1.in" and output file "data1.out" - IO(file_prefix="data",input_suffix=".input") -> create input file "data.input" and output file "data.out" - IO(file_prefix="data",output_suffix=".output") -> create input file "data.in" and output file "data.output" - IO(file_prefix="data",data_id=2,input_suffix=".input") -> create input file "data2.input" and output file "data2.out" - IO("data{}.in","data{}.out",data_id=2) -> create input file "data2.in" and output file "data2.out" - IO(open('data.in', 'w+'), open('data.out', 'w+')) -> input file "data.in" and output file "data.out" + input_file, output_file overload: + None -> make a temp file (if file_prefix is None) + file object -> treat the file-like object as in/output file + int -> open file by file descriptor + str -> a filename or filename template like 'awd{}.in'. ``{}`` will be replaced by ``data_id`` + int data_id -> the id of the data. if it's None, the file names will not contain the id. + legacy argumants: + str file_prefix -> the prefix for the input and output files + str input_suffix = ".in" -> the suffix of the input file + str output_suffix = ".out" -> the suffix of the output file + disable_output -> bool, set to True to disable output + Examples: + IO("a","b") -> create input file "a" and output file "b" + IO("a.in","b.out") -> create input file "a.in" and output file "b.out" + IO(file_prefix="data") -> create input file "data.in" and output file "data.out" + IO(file_prefix="data",data_id=1) -> create input file "data1.in" and output file "data1.out" + IO(file_prefix="data",input_suffix=".input") -> create input file "data.input" and output file "data.out" + IO(file_prefix="data",output_suffix=".output") -> create input file "data.in" and output file "data.output" + IO(file_prefix="data",data_id=2,input_suffix=".input") -> create input file "data2.input" and output file "data2.out" + IO("data{}.in","data{}.out",data_id=2) -> create input file "data2.in" and output file "data2.out" + IO(open('data.in', 'w+'), open('data.out', 'w+')) -> input file "data.in" and output file "data.out" """ if file_prefix is not None: # legacy mode - input_file = '{}{{}}{}'.format(self.__escape_format(file_prefix), self.__escape_format(input_suffix)) - output_file = '{}{{}}{}'.format(self.__escape_format(file_prefix), self.__escape_format(output_suffix)) + input_file = "{}{{}}{}".format( + self.__escape_format(file_prefix), self.__escape_format(input_suffix) + ) + output_file = "{}{{}}{}".format( + self.__escape_format(file_prefix), self.__escape_format(output_suffix) + ) self.input_filename, self.output_filename = None, None self.__input_temp, self.__output_temp = False, False - self.__init_file(input_file, data_id, 'i') + self.__init_file(input_file, data_id, "i") if not disable_output: - self.__init_file(output_file, data_id, 'o') + self.__init_file(output_file, data_id, "o") else: self.output_file = None self.__closed = False @@ -55,34 +69,34 @@ def __init_file(self, f, data_id, file_type): is_file = False if isinstance(f, IOBase) or is_file: # consider ``f`` as a file object - if file_type == 'i': + if file_type == "i": self.input_file = f else: self.output_file = f elif isinstance(f, int): # consider ``f`` as a file descor - self.__init_file(open(f, 'w+', newline='\n'), data_id, file_type) + self.__init_file(open(f, "w+", newline="\n"), data_id, file_type) elif f is None: # consider wanna temp file fd, self.input_filename = tempfile.mkstemp() self.__init_file(fd, data_id, file_type) - if file_type == 'i': + if file_type == "i": self.__input_temp = True else: self.__output_temp = True else: # consider ``f`` as filename template - filename = f.format(data_id or '') - if file_type == 'i': + filename = f.format(data_id or "") + if file_type == "i": self.input_filename = filename log.debug("Processing %s" % self.input_filename) else: self.output_filename = filename - self.__init_file(open(filename, 'w+', newline='\n'), data_id, file_type) + self.__init_file(open(filename, "w+", newline="\n"), data_id, file_type) def __escape_format(self, st): """replace "{}" to "{{}}" """ - return re.sub(r'\{', '{{', re.sub(r'\}', '}}', st)) + return re.sub(r"\{", "{{", re.sub(r"\}", "}}", st)) def __del_files(self): """delete files""" @@ -125,10 +139,10 @@ def __exit__(self, exc_type, exc_val, exc_tb): def __write(self, file, *args, **kwargs): """__write(self, file, *args, **kwargs) -> None - Write every element in *args into file. If the element isn't "\n", insert a space. It will convert every element into str - file file -> the file object to write - **kwargs: - str separator = " " -> a string used to separate every element + Write every element in *args into file. If the element isn't "\n", insert a space. It will convert every element into str + file file -> the file object to write + **kwargs: + str separator = " " -> a string used to separate every element """ separator = kwargs.get("separator", " ") for arg in args: @@ -144,17 +158,17 @@ def __write(self, file, *args, **kwargs): def input_write(self, *args, **kwargs): """input_write(self, *args, **kwargs) -> None - Write every element in *args into the input file. Splits with spaces. It will convert every element into string - **kwargs: - str separator = " " -> a string used to separate every element + Write every element in *args into the input file. Splits with spaces. It will convert every element into string + **kwargs: + str separator = " " -> a string used to separate every element """ self.__write(self.input_file, *args, **kwargs) def input_writeln(self, *args, **kwargs): """input_writeln(self, *args, **kwargs) -> None - Write every element in *args into the input file and turn to a new line. Splits with spaces. It will convert every element into string - **kwargs: - str separator = " " -> a string used to separate every element + Write every element in *args into the input file and turn to a new line. Splits with spaces. It will convert every element into string + **kwargs: + str separator = " " -> a string used to separate every element """ args = list(args) args.append("\n") @@ -162,30 +176,36 @@ def input_writeln(self, *args, **kwargs): def output_gen(self, shell_cmd): """output_gen(self, shell_cmd) -> None - Run the command shell_cmd(usually the std programme) and send it the input file as stdin. Write its output to the output file. - str shell_cmd -> the command to run, usually the std programme + Run the command shell_cmd(usually the std programme) and send it the input file as stdin. Write its output to the output file. + str shell_cmd -> the command to run, usually the std programme """ self.flush_buffer() origin_pos = self.input_file.tell() self.input_file.seek(0) - subprocess.check_call(shell_cmd, shell=True, stdin=self.input_file, stdout=self.output_file, universal_newlines=True) + subprocess.check_call( + shell_cmd, + shell=True, + stdin=self.input_file, + stdout=self.output_file, + universal_newlines=True, + ) self.input_file.seek(origin_pos) log.debug(self.output_filename, " done") def output_write(self, *args, **kwargs): """output_write(self, *args, **kwargs) -> None - Write every element in *args into the output file. Splits with spaces. It will convert every element into string - **kwargs: - str separator = " " -> a string used to separate every element + Write every element in *args into the output file. Splits with spaces. It will convert every element into string + **kwargs: + str separator = " " -> a string used to separate every element """ self.__write(self.output_file, *args, **kwargs) def output_writeln(self, *args, **kwargs): """output_writeln(self, *args, **kwargs) -> None - Write every element in *args into the output file and turn to a new line. Splits with spaces. It will convert every element into string - **kwargs: - str separator = " " -> a string used to separate every element + Write every element in *args into the output file and turn to a new line. Splits with spaces. It will convert every element into string + **kwargs: + str separator = " " -> a string used to separate every element """ args = list(args) args.append("\n") diff --git a/cyaron/log.py b/cyaron/log.py index 33b3b0c..2dbb922 100644 --- a/cyaron/log.py +++ b/cyaron/log.py @@ -2,24 +2,30 @@ from functools import partial import sys from threading import Lock + try: import colorful except ImportError: + class colorful: def __getattr__(self, attr): return lambda st: st + colorful = colorful() from .utils import make_unicode __print = print + + def _print(*args, **kwargs): flush = False - if 'flush' in kwargs: - flush = kwargs['flush'] - del kwargs['flush'] + if "flush" in kwargs: + flush = kwargs["flush"] + del kwargs["flush"] __print(*args, **kwargs) if flush: - kwargs.get('file', sys.stdout).flush() + kwargs.get("file", sys.stdout).flush() + def _join_dict(a, b): """join two dict""" @@ -28,8 +34,11 @@ def _join_dict(a, b): c[k] = v return c + _log_funcs = {} _log_lock = Lock() + + def log(funcname, *args, **kwargs): """log with log function specified by ``funcname``""" _log_lock.acquire() @@ -37,6 +46,7 @@ def log(funcname, *args, **kwargs): _log_lock.release() return rv + """5 log levels 1. debug: debug info 2. info: common info @@ -45,11 +55,12 @@ def log(funcname, *args, **kwargs): 5. error: errors """ -debug = partial(log, 'debug') -info = partial(log, 'info') -print = partial(log, 'print') -warn = partial(log, 'warn') -error = partial(log, 'error') +debug = partial(log, "debug") +info = partial(log, "info") +print = partial(log, "print") +warn = partial(log, "warn") +error = partial(log, "error") + def register_logfunc(funcname, func): """register logfunc @@ -64,10 +75,21 @@ def register_logfunc(funcname, func): except KeyError: pass -_nb_print = lambda *args, **kwargs: _print(*args, **_join_dict(kwargs, {'flush': True})) -_nb_print_e = lambda *args, **kwargs: _print(*args, **_join_dict(kwargs, {'file': sys.stderr, 'flush': True})) -_cl_print = lambda color, *args, **kwargs: _nb_print(*[color(make_unicode(item)) for item in args], **kwargs) if sys.stdout.isatty() else _nb_print(*args, **kwargs) -_cl_print_e = lambda color, *args, **kwargs: _nb_print_e(*[color(make_unicode(item)) for item in args], **kwargs) if sys.stderr.isatty() else _nb_print_e(*args, **kwargs) + +_nb_print = lambda *args, **kwargs: _print(*args, **_join_dict(kwargs, {"flush": True})) +_nb_print_e = lambda *args, **kwargs: _print( + *args, **_join_dict(kwargs, {"file": sys.stderr, "flush": True}) +) +_cl_print = lambda color, *args, **kwargs: ( + _nb_print(*[color(make_unicode(item)) for item in args], **kwargs) + if sys.stdout.isatty() + else _nb_print(*args, **kwargs) +) +_cl_print_e = lambda color, *args, **kwargs: ( + _nb_print_e(*[color(make_unicode(item)) for item in args], **kwargs) + if sys.stderr.isatty() + else _nb_print_e(*args, **kwargs) +) _default_debug = partial(_cl_print, colorful.cyan) _default_info = partial(_cl_print, colorful.blue) @@ -75,28 +97,32 @@ def register_logfunc(funcname, func): _default_warn = partial(_cl_print_e, colorful.yellow) _default_error = partial(_cl_print_e, colorful.red) + def set_quiet(): """set log mode to "quiet" """ - register_logfunc('debug', None) - register_logfunc('info', None) - register_logfunc('print', _default_print) - register_logfunc('warn', None) - register_logfunc('error', _default_error) + register_logfunc("debug", None) + register_logfunc("info", None) + register_logfunc("print", _default_print) + register_logfunc("warn", None) + register_logfunc("error", _default_error) + def set_normal(): """set log mode to "normal" """ - register_logfunc('debug', None) - register_logfunc('info', _default_info) - register_logfunc('print', _default_print) - register_logfunc('warn', _default_warn) - register_logfunc('error', _default_error) + register_logfunc("debug", None) + register_logfunc("info", _default_info) + register_logfunc("print", _default_print) + register_logfunc("warn", _default_warn) + register_logfunc("error", _default_error) + def set_verbose(): """set log mode to "verbose" """ - register_logfunc('debug', _default_debug) - register_logfunc('info', _default_info) - register_logfunc('print', _default_print) - register_logfunc('warn', _default_warn) - register_logfunc('error', _default_error) + register_logfunc("debug", _default_debug) + register_logfunc("info", _default_info) + register_logfunc("print", _default_print) + register_logfunc("warn", _default_warn) + register_logfunc("error", _default_error) + set_normal() diff --git a/cyaron/math.py b/cyaron/math.py index eb7379d..a23d332 100644 --- a/cyaron/math.py +++ b/cyaron/math.py @@ -1,7 +1,7 @@ -#coding=utf8 -''' +# coding=utf8 +""" forked from https://blog.dreamshire.com/common-functions-routines-project-euler/ -''' +""" from __future__ import absolute_import from math import sqrt, ceil, gcd from functools import reduce @@ -10,8 +10,10 @@ fact = (1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880) -def help(): #Give help informations - help_txt=""" + + +def help(): # Give help informations + help_txt = """ Welcome to CYaRon/math.py help! Functions are: | factorial(n) - The factorial of n @@ -40,49 +42,73 @@ def help(): #Give help informations """ print(help_txt) -def factorial(n): return reduce(lambda x,y:x*y,range(1,n+1),1) -def is_perm(a,b): return sorted(str(a))==sorted(str(b)) +def factorial(n): + return reduce(lambda x, y: x * y, range(1, n + 1), 1) + + +def is_perm(a, b): + return sorted(str(a)) == sorted(str(b)) + -def is_palindromic(n): n=str(n); return n==n[::-1] +def is_palindromic(n): + n = str(n) + return n == n[::-1] -def is_pandigital(n, s=9): n=str(n); return len(n)==s and not '1234567890'[:s].strip(n) -#--- Calculate the sum of proper divisors for n-------------------------------------------------- +def is_pandigital(n, s=9): + n = str(n) + return len(n) == s and not "1234567890"[:s].strip(n) + + +# --- Calculate the sum of proper divisors for n-------------------------------------------------- def d(n): s = 1 t = sqrt(n) - for i in range(2, int(t)+1): - if n % i == 0: s += i + n/i - if t == int(t): s -= t #correct s if t is a perfect square + for i in range(2, int(t) + 1): + if n % i == 0: + s += i + n / i + if t == int(t): + s -= t # correct s if t is a perfect square return s -#--- Create a list of all palindromic numbers with k digits-------------------------------------- + +# --- Create a list of all palindromic numbers with k digits-------------------------------------- def pal_list(k): if k == 1: return [1, 2, 3, 4, 5, 6, 7, 8, 9] - return [sum([n*(10**i) for i,n in enumerate(([x]+list(ys)+[z]+list(ys)[::-1]+[x]) if k%2 - else ([x]+list(ys)+list(ys)[::-1]+[x]))]) - for x in range(1,10) - for ys in itertools.product(range(10), repeat=int(k/2)-1) - for z in (range(10) if k%2 else (None,))] - - -#--- sum of factorial's digits------------------------------------------------------------------- + return [ + sum( + [ + n * (10**i) + for i, n in enumerate( + ([x] + list(ys) + [z] + list(ys)[::-1] + [x]) + if k % 2 + else ([x] + list(ys) + list(ys)[::-1] + [x]) + ) + ] + ) + for x in range(1, 10) + for ys in itertools.product(range(10), repeat=int(k / 2) - 1) + for z in (range(10) if k % 2 else (None,)) + ] + + +# --- sum of factorial's digits------------------------------------------------------------------- def sof_digits(n): - if n==0: return 1 + if n == 0: + return 1 s = 0 while n > 0: s, n = s + fact[n % 10], n // 10 return s - -#--- find the nth Fibonacci number--------------------------------------------------------------- +# --- find the nth Fibonacci number--------------------------------------------------------------- def fibonacci(n): """ Find the nth number in the Fibonacci series. Example: - + >>>fibonacci(100) 354224848179261915075 @@ -94,6 +120,7 @@ def fibonacci(n): raise ValueError("Negative arguments not implemented") return _fib(n)[0] + # Returns a tuple (F(n), F(n+1)) def _fib(n): if n == 0: @@ -108,38 +135,40 @@ def _fib(n): return (d, c + d) -#--- sum of squares of digits------------------------------------------------------------------- +# --- sum of squares of digits------------------------------------------------------------------- def sos_digits(n): s = 0 while n > 0: - s, n = s + (n % 10)**2, n // 10 + s, n = s + (n % 10) ** 2, n // 10 return s -#--- sum of the digits to a power e------------------------------------------------------------- + +# --- sum of the digits to a power e------------------------------------------------------------- def pow_digits(n, e): s = 0 while n > 0: - s, n = s + (n % 10)**e, n // 10 + s, n = s + (n % 10) ** e, n // 10 return s - -#--- check n for prime-------------------------------------------------------------------------- +# --- check n for prime-------------------------------------------------------------------------- def is_prime(n): - if n <= 1: return False - if n <= 3: return True - if n%2==0 or n%3 == 0: return False + if n <= 1: + return False + if n <= 3: + return True + if n % 2 == 0 or n % 3 == 0: + return False r = int(sqrt(n)) f = 5 while f <= r: - if n%f == 0 or n%(f+2) == 0: return False - f+= 6 + if n % f == 0 or n % (f + 2) == 0: + return False + f += 6 return True - - -#--- Miller-Rabin primality test---------------------------------------------------------------- +# --- Miller-Rabin primality test---------------------------------------------------------------- def miller_rabin(n): """ Check n for primalty: Example: @@ -163,33 +192,33 @@ def miller_rabin(n): return False return True + def miller_rabin_pass(a, s, d, n): a_to_power = pow(a, d, n) if a_to_power == 1: return True - for i in range(s-1): + for i in range(s - 1): if a_to_power == n - 1: return True a_to_power = (a_to_power * a_to_power) % n return a_to_power == n - 1 - -#--- factor a number into primes and frequency---------------------------------------------------- +# --- factor a number into primes and frequency---------------------------------------------------- def factor(n): """ - find the prime factors of n along with their frequencies. Example: + find the prime factors of n along with their frequencies. Example: - >>> factor(786456) - [(2,3), (3,3), (11,1), (331,1)] + >>> factor(786456) + [(2,3), (3,3), (11,1), (331,1)] - Source: Project Euler forums for problem #3 + Source: Project Euler forums for problem #3 """ f, factors, prime_gaps = 1, [], [2, 4, 2, 4, 6, 2, 6, 4] if n < 1: return [] while True: - for gap in ([1, 1, 2, 2, 4] if f < 11 else prime_gaps): + for gap in [1, 1, 2, 2, 4] if f < 11 else prime_gaps: f += gap if f * f > n: # If f > sqrt(n) if n == 1: @@ -205,7 +234,7 @@ def factor(n): factors.append((f, e)) -#--- generate permutations----------------------------------------------------------------------- +# --- generate permutations----------------------------------------------------------------------- def perm(n, s): """ requires function factorial() @@ -214,43 +243,41 @@ def perm(n, s): >>>perm(30, 'abcde') bcade """ - if len(s)==1: return s - q, r = divmod(n, factorial(len(s)-1)) - return s[q] + perm(r, s[:q] + s[q+1:]) - + if len(s) == 1: + return s + q, r = divmod(n, factorial(len(s) - 1)) + return s[q] + perm(r, s[:q] + s[q + 1 :]) - -#--- binomial coefficients----------------------------------------------------------------------- +# --- binomial coefficients----------------------------------------------------------------------- def binomial(n, k): """ Calculate C(n,k), the number of ways can k be chosen from n. Example: - + >>>binomial(30,12) 86493225 """ nt = 1 - for t in range(min(k, n-k)): - nt = nt * (n-t) // (t+1) + for t in range(min(k, n - k)): + nt = nt * (n - t) // (t + 1) return nt -#--- catalan number------------------------------------------------------------------------------ +# --- catalan number------------------------------------------------------------------------------ def catalan_number(n): """ Calculate the nth Catalan number. Example: - + >>>catalan_number(10) 16796 """ nm = dm = 1 - for k in range(2, n+1): - nm, dm = (nm*(n+k), dm*k) + for k in range(2, n + 1): + nm, dm = (nm * (n + k), dm * k) return nm / dm - -#--- generate prime numbers---------------------------------------------------------------------- +# --- generate prime numbers---------------------------------------------------------------------- def prime_sieve(n): """ Return a list of prime numbers from 2 to a prime < n. Very fast (n<10,000,000) in 0.4 sec. @@ -262,16 +289,15 @@ def prime_sieve(n): Algorithm & Python source: Robert William Hanks http://stackoverflow.com/questions/17773352/python-sieve-prime-numbers """ - sieve = [True] * (n//2) - for i in range(3,int(n**0.5)+1,2): - if sieve[i//2]: - sieve[i*i//2::i] = [False] * ((n-i*i-1)//(2*i)+1) - return [2] + [2*i+1 for i in range(1,n//2) if sieve[i]] + sieve = [True] * (n // 2) + for i in range(3, int(n**0.5) + 1, 2): + if sieve[i // 2]: + sieve[i * i // 2 :: i] = [False] * ((n - i * i - 1) // (2 * i) + 1) + return [2] + [2 * i + 1 for i in range(1, n // 2) if sieve[i]] - -#--- bezout coefficients-------------------------------------------------------------------------- -def exgcd(a,b): +# --- bezout coefficients-------------------------------------------------------------------------- +def exgcd(a, b): """ Bézout coefficients (u,v) of (a,b) as: @@ -287,85 +313,131 @@ def exgcd(a,b): Algorithm source: Pierre L. Douillet http://www.douillet.info/~douillet/working_papers/bezout/node2.html """ - u, v, s, t = 1, 0, 0, 1 - while b !=0: - q, r = divmod(a,b) + u, v, s, t = 1, 0, 0, 1 + while b != 0: + q, r = divmod(a, b) a, b = b, r - u, s = s, u - q*s - v, t = t, v - q*t + u, s = s, u - q * s + v, t = t, v - q * t return (u, v, a) - - -def mod_inverse(a,b): - x,y,z = exgcd(a,b) - return x; + +def mod_inverse(a, b): + x, y, z = exgcd(a, b) + return x + def phi(x): - if x==1: - return 1; - factors = factor(x); - ans = x; + if x == 1: + return 1 + factors = factor(x) + ans = x for prime in factors: - ans=int(ans / prime[0]*(prime[0]-1)) + ans = int(ans / prime[0] * (prime[0] - 1)) return ans + def miu(x): - if x==1: - return 1; + if x == 1: + return 1 factors = factor(x) for prime in factors: - if prime[1]>1: - return 0; - return 1-(len(factors) and 1)*2 - - -#--- number base conversion ------------------------------------------------------------------- -#source: http://interactivepython.org/runestone/static/pythonds/Recursion/pythondsConvertinganIntegertoaStringinAnyBase.html -def dec2base(n,base): - convertString = "0123456789ABCDEF" - if n < base: - return convertString[n] - else: - return dec2base(n//base,base) + convertString[n%base] - -#--- number to words ---------------------------------------------------------------------------- -#this function copied from stackoverflow user: Developer, Oct 5 '13 at 3:45 -def n2words(num,join=True): - '''words = {} convert an integer number into words''' - units = ['','One','Two','Three','Four','Five','Six','Seven','Eight','Nine'] - teens = ['','Eleven','Twelve','Thirteen','Fourteen','Fifteen','Sixteen', \ - 'Seventeen','Eighteen','Nineteen'] - tens = ['','Ten','Twenty','Thirty','Forty','Fifty','Sixty','Seventy', \ - 'Eighty','Ninety'] - thousands = ['','Thousand','Million','Billion','Trillion','Quadrillion', \ - 'Quintillion','Sextillion','Septillion','Octillion', \ - 'Nonillion','Decillion','Undecillion','Duodecillion', \ - 'Tredecillion','Quattuordecillion','Sexdecillion', \ - 'Septendecillion','Octodecillion','Novemdecillion', \ - 'Vigintillion'] + if prime[1] > 1: + return 0 + return 1 - (len(factors) and 1) * 2 + + +# --- number base conversion ------------------------------------------------------------------- +# source: http://interactivepython.org/runestone/static/pythonds/Recursion/pythondsConvertinganIntegertoaStringinAnyBase.html +def dec2base(n, base): + convertString = "0123456789ABCDEF" + if n < base: + return convertString[n] + else: + return dec2base(n // base, base) + convertString[n % base] + + +# --- number to words ---------------------------------------------------------------------------- +# this function copied from stackoverflow user: Developer, Oct 5 '13 at 3:45 +def n2words(num, join=True): + """words = {} convert an integer number into words""" + units = ["", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"] + teens = [ + "", + "Eleven", + "Twelve", + "Thirteen", + "Fourteen", + "Fifteen", + "Sixteen", + "Seventeen", + "Eighteen", + "Nineteen", + ] + tens = [ + "", + "Ten", + "Twenty", + "Thirty", + "Forty", + "Fifty", + "Sixty", + "Seventy", + "Eighty", + "Ninety", + ] + thousands = [ + "", + "Thousand", + "Million", + "Billion", + "Trillion", + "Quadrillion", + "Quintillion", + "Sextillion", + "Septillion", + "Octillion", + "Nonillion", + "Decillion", + "Undecillion", + "Duodecillion", + "Tredecillion", + "Quattuordecillion", + "Sexdecillion", + "Septendecillion", + "Octodecillion", + "Novemdecillion", + "Vigintillion", + ] words = [] - if num==0: words.append('zero') + if num == 0: + words.append("zero") else: - numStr = '%d'%num + numStr = "%d" % num numStrLen = len(numStr) - groups = int((numStrLen+2)/3) - numStr = numStr.zfill(groups*3) - for i in range(0,groups*3,3): - h,t,u = int(numStr[i]),int(numStr[i+1]),int(numStr[i+2]) - g = groups-(int(i/3)+1) - if h>=1: + groups = int((numStrLen + 2) / 3) + numStr = numStr.zfill(groups * 3) + for i in range(0, groups * 3, 3): + h, t, u = int(numStr[i]), int(numStr[i + 1]), int(numStr[i + 2]) + g = groups - (int(i / 3) + 1) + if h >= 1: words.append(units[h]) - words.append('Hundred') - if t>1: + words.append("Hundred") + if t > 1: words.append(tens[t]) - if u>=1: words.append(units[u]) - elif t==1: - if u>=1: words.append(teens[u]) - else: words.append(tens[t]) + if u >= 1: + words.append(units[u]) + elif t == 1: + if u >= 1: + words.append(teens[u]) + else: + words.append(tens[t]) else: - if u>=1: words.append(units[u]) - if (g>=1) and ((h+t+u)>0): words.append(thousands[g]+'') - if join: return ' '.join(words) + if u >= 1: + words.append(units[u]) + if (g >= 1) and ((h + t + u) > 0): + words.append(thousands[g] + "") + if join: + return " ".join(words) return words diff --git a/cyaron/merger.py b/cyaron/merger.py index e7069b6..41847c3 100644 --- a/cyaron/merger.py +++ b/cyaron/merger.py @@ -1,55 +1,57 @@ from .graph import * + class Merger: def __init__(self, *graphs, **kwargs): """__init__(self, *graphs, **kwargs) -> None - put several graphs into one - list graphs -> the graphs that will be merged - list kwargs: - None + put several graphs into one + list graphs -> the graphs that will be merged + list kwargs: + None """ self.graphs = graphs self.G = Graph(sum([len(i.edges) - 1 for i in graphs]), graphs[0].directed) - - counter = 0 + + counter = 0 for graph in self.graphs: graph.offset = counter for edge in graph.iterate_edges(): - self.G.add_edge(edge.start + counter, - edge.end + counter, - weight=edge.weight) + self.G.add_edge( + edge.start + counter, edge.end + counter, weight=edge.weight + ) counter += len(graph.edges) - 1 def __add_edge(self, u, v, **kwargs): """__add_edge(self, u, v, **kwargs) - tuple u -> (graph_index, vertex) indicating the start point - tuple v -> (graph_index, vertex) indicating the end point - **kwargs: - int weight -> edge weight + tuple u -> (graph_index, vertex) indicating the start point + tuple v -> (graph_index, vertex) indicating the end point + **kwargs: + int weight -> edge weight """ - self.G.add_edge(self.graphs[ u[0] ].offset + u[1], - self.graphs[ v[0] ].offset + v[1], - weight=kwargs.get("weight", 1)) - + self.G.add_edge( + self.graphs[u[0]].offset + u[1], + self.graphs[v[0]].offset + v[1], + weight=kwargs.get("weight", 1), + ) + def add_edge(self, u, v, **kwargs): - """add_edge(self, u, v, **kwargs) -> None - """ + """add_edge(self, u, v, **kwargs) -> None""" self.__add_edge(u, v, **kwargs) def to_str(self, **kwargs): return self.G.to_str(**kwargs) - + def __str__(self): return self.to_str() @staticmethod def component(point_count, edge_count, **kwargs): """component(point_count, edge_count, **kwargs) - generate a graph with certain components - int point_count -> the number of vertices of each component - int edge_count -> the number of edges of each component - **kwargs: - int component_count -> indicating how many components there are + generate a graph with certain components + int point_count -> the number of vertices of each component + int edge_count -> the number of edges of each component + **kwargs: + int component_count -> indicating how many components there are """ component_count = kwargs.get("component_count", (2, 2)) if not list_like(component_count): diff --git a/cyaron/output_capture.py b/cyaron/output_capture.py index 4bcac58..5680526 100644 --- a/cyaron/output_capture.py +++ b/cyaron/output_capture.py @@ -1,10 +1,12 @@ from contextlib import contextmanager import sys + try: from StringIO import StringIO except ImportError: from io import StringIO + @contextmanager def captured_output(): new_out, new_err = StringIO(), StringIO() @@ -13,4 +15,4 @@ def captured_output(): sys.stdout, sys.stderr = new_out, new_err yield sys.stdout, sys.stderr finally: - sys.stdout, sys.stderr = old_out, old_err \ No newline at end of file + sys.stdout, sys.stderr = old_out, old_err diff --git a/cyaron/polygon.py b/cyaron/polygon.py index d0dfca8..d73fdec 100644 --- a/cyaron/polygon.py +++ b/cyaron/polygon.py @@ -6,7 +6,7 @@ class Polygon: - def __init__(self,points=[]): + def __init__(self, points=[]): if not list_like(points): raise Exception("polygon must be constructed by a list of points") self.points = points @@ -15,15 +15,16 @@ def __str__(self): buf = [] for point in self.points: buf.append(str(point[0]) + " " + str(point[1])) - return '\n'.join(buf) + return "\n".join(buf) def perimeter(self): ans = 0 for i in range(0, len(self.points)): a = self.points[i] b = self.points[(i + 1) % len(self.points)] - ans = ans + math.sqrt((a[0] - b[0]) * (a[0] - b[0]) + - (a[1] - b[1]) * (a[1] - b[1])) + ans = ans + math.sqrt( + (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) + ) return ans def area(self): @@ -37,8 +38,8 @@ def area(self): ans = ans / 2.0 return ans - #generate a convex hull with n points - #it's possible to have even edges + # generate a convex hull with n points + # it's possible to have even edges @staticmethod def convex_hull(n, **kwargs): # fx, fy are functions which map [0,1] to int or float @@ -75,8 +76,7 @@ def convex_hull(n, **kwargs): a = st[len(st) - 1] b = points[i] o = st[len(st) - 2] - tmp = (a[0] - o[0]) * (b[1] - o[1]) - \ - (a[1] - o[1]) * (b[0] - o[0]) + tmp = (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) if tmp > 0 or (tmp == 0 and not strict): break st.pop() @@ -87,8 +87,7 @@ def convex_hull(n, **kwargs): a = st[len(st) - 1] b = points[i] o = st[len(st) - 2] - tmp = (a[0] - o[0]) * (b[1] - o[1]) - \ - (a[1] - o[1]) * (b[0] - o[0]) + tmp = (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) if tmp > 0 or (tmp == 0 and not strict): break st.pop() @@ -110,21 +109,34 @@ def __conquer(points): if len(points) <= 2: return points if len(points) == 3: - (points[1],points[2])=(points[2],points[1]) + (points[1], points[2]) = (points[2], points[1]) return points divide_id = random.randint(2, len(points) - 1) divide_point1 = points[divide_id] divide_k = random.uniform(0.01, 0.99) - divide_point2 = [divide_k * (points[1][0] - points[0][0]) + points[0][0], - divide_k * (points[1][1] - points[0][1]) + points[0][1]] + divide_point2 = [ + divide_k * (points[1][0] - points[0][0]) + points[0][0], + divide_k * (points[1][1] - points[0][1]) + points[0][1], + ] # path: points[0]->points[divide]->points[1] # dividing line in the form Ax+By+C=0 - divide_line = [divide_point2[1] - divide_point1[1], - divide_point1[0] - divide_point2[0], - -divide_point1[0] * divide_point2[1] - + divide_point1[1] * divide_point2[0]] - p0 = (divide_line[0] * points[0][0] + divide_line[1] * points[0][1] + divide_line[2] >= 0) - p1 = (divide_line[0] * points[1][0] + divide_line[1] * points[1][1] + divide_line[2] >= 0) + divide_line = [ + divide_point2[1] - divide_point1[1], + divide_point1[0] - divide_point2[0], + -divide_point1[0] * divide_point2[1] + divide_point1[1] * divide_point2[0], + ] + p0 = ( + divide_line[0] * points[0][0] + + divide_line[1] * points[0][1] + + divide_line[2] + >= 0 + ) + p1 = ( + divide_line[0] * points[1][0] + + divide_line[1] * points[1][1] + + divide_line[2] + >= 0 + ) if p0 == p1: # the divide point isn't good enough... return Polygon.__conquer(points) s = [[], []] @@ -135,7 +147,12 @@ def __conquer(points): for i in range(2, len(points)): if i == divide_id: continue - pt = (divide_line[0] * points[i][0] + divide_line[1] * points[i][1] + divide_line[2] >= 0) + pt = ( + divide_line[0] * points[i][0] + + divide_line[1] * points[i][1] + + divide_line[2] + >= 0 + ) s[pt].append(points[i]) pa = Polygon.__conquer(s[p0]) pb = Polygon.__conquer(s[not p0]) @@ -152,17 +169,23 @@ def simple_polygon(points): if len(points) <= 3: return Polygon(points) # divide by points[0], points[1] - divide_line = [points[1][1] - points[0][1], - points[0][0] - points[1][0], - -points[0][0] * points[1][1] - + points[0][1] * points[1][0]] + divide_line = [ + points[1][1] - points[0][1], + points[0][0] - points[1][0], + -points[0][0] * points[1][1] + points[0][1] * points[1][0], + ] s = [[], []] s[0].append(points[0]) s[0].append(points[1]) s[1].append(points[1]) s[1].append(points[0]) for i in range(2, len(points)): - pt = (divide_line[0] * points[i][0] + divide_line[1] * points[i][1] + divide_line[2] >= 0) + pt = ( + divide_line[0] * points[i][0] + + divide_line[1] * points[i][1] + + divide_line[2] + >= 0 + ) s[pt].append(points[i]) pa = Polygon.__conquer(s[0]) pb = Polygon.__conquer(s[1]) diff --git a/cyaron/sequence.py b/cyaron/sequence.py index 625456e..c584a33 100644 --- a/cyaron/sequence.py +++ b/cyaron/sequence.py @@ -1,13 +1,13 @@ from .utils import * + class Sequence: - """Class Sequence: the tool class for sequences. - """ + """Class Sequence: the tool class for sequences.""" def __init__(self, formula, initial_values=()): """__init__(self, formula, initial_values=() -> None - Create a sequence object. - int formula(int, function) -> the formula function ... + Create a sequence object. + int formula(int, function) -> the formula function ... """ if not callable(formula): raise Exception("formula must be a function") @@ -30,4 +30,4 @@ def get(self, left_range, right_range=None): if right_range is None: return self.__get_one(left_range) - return [self.__get_one(i) for i in range(left_range, right_range+1)] + return [self.__get_one(i) for i in range(left_range, right_range + 1)] diff --git a/cyaron/string.py b/cyaron/string.py index 6af1e5c..2a5694e 100644 --- a/cyaron/string.py +++ b/cyaron/string.py @@ -53,7 +53,9 @@ def random_sentence(word_count_range, **kwargs): def random_paragraph(sentence_count_range, **kwargs): sentence_count = sentence_count_range if list_like(sentence_count_range): - sentence_count = random.randint(sentence_count_range[0], sentence_count_range[1]) + sentence_count = random.randint( + sentence_count_range[0], sentence_count_range[1] + ) word_count_range = kwargs.get("word_count_range", (6, 10)) @@ -95,15 +97,18 @@ def random_paragraph(sentence_count_range, **kwargs): sentences.append(string) - paragraph = reduce(lambda x, y: x + random.choice(sentence_joiners) + y, sentences) + paragraph = reduce( + lambda x, y: x + random.choice(sentence_joiners) + y, sentences + ) return paragraph @staticmethod def random_regular(*args, **kwargs): pattern = args limit_len = int(kwargs.get("limit", "10")) - if (limit_len <= 1): limit_len = 10 - if (list_like(args)): + if limit_len <= 1: + limit_len = 10 + if list_like(args): pattern = random.choice(args) _x = xeger.Xeger(limit=limit_len) return _x.xeger(pattern) diff --git a/cyaron/tests/compare_test.py b/cyaron/tests/compare_test.py index f820a9d..01d4354 100644 --- a/cyaron/tests/compare_test.py +++ b/cyaron/tests/compare_test.py @@ -11,6 +11,7 @@ log.set_verbose() + class TestCompare(unittest.TestCase): def setUp(self): @@ -51,16 +52,19 @@ def test_noipstyle_incorrect(self): with captured_output() as (out, err): Compare.output("test_another_incorrect.out", std=io) except CompareMismatch as e: - self.assertEqual(e.name, 'test_another_incorrect.out') + self.assertEqual(e.name, "test_another_incorrect.out") e = e.mismatch - self.assertEqual(e.content, 'test123\r\ntest124 ') - self.assertEqual(e.std, 'test123 \ntest123\n\n') - self.assertEqual(str(e), 'On line 2 column 7, read 4, expected 3.') + self.assertEqual(e.content, "test123\r\ntest124 ") + self.assertEqual(e.std, "test123 \ntest123\n\n") + self.assertEqual(str(e), "On line 2 column 7, read 4, expected 3.") else: self.assertTrue(False) result = out.getvalue().strip() - self.assertEqual(result, "test_another_incorrect.out: !!!INCORRECT!!! On line 2 column 7, read 4, expected 3.") + self.assertEqual( + result, + "test_another_incorrect.out: !!!INCORRECT!!! On line 2 column 7, read 4, expected 3.", + ) def test_fulltext_program(self): with open("correct.py", "w") as f: @@ -77,19 +81,31 @@ def test_fulltext_program(self): try: with captured_output() as (out, err): - Compare.program("python correct.py", "python incorrect.py", std=io, input=io, grader="FullText") + Compare.program( + "python correct.py", + "python incorrect.py", + std=io, + input=io, + grader="FullText", + ) except CompareMismatch as e: - self.assertEqual(e.name, 'python incorrect.py') + self.assertEqual(e.name, "python incorrect.py") e = e.mismatch - self.assertEqual(e.content, '2\n') - self.assertEqual(e.std, '1\n') - self.assertEqual(e.content_hash, '53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3') - self.assertEqual(e.std_hash, '4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865') + self.assertEqual(e.content, "2\n") + self.assertEqual(e.std, "1\n") + self.assertEqual( + e.content_hash, + "53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3", + ) + self.assertEqual( + e.std_hash, + "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865", + ) else: self.assertTrue(False) result = out.getvalue().strip() - correct_out = 'python correct.py: Correct \npython incorrect.py: !!!INCORRECT!!! Hash mismatch: read 53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3, expected 4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865' + correct_out = "python correct.py: Correct \npython incorrect.py: !!!INCORRECT!!! Hash mismatch: read 53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3, expected 4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" self.assertEqual(result, correct_out) def test_file_input(self): @@ -106,28 +122,38 @@ def test_file_input(self): io.input_writeln("233") with captured_output() as (out, err): - Compare.program("python correct.py", std_program="python std.py", input=io, grader="NOIPStyle") + Compare.program( + "python correct.py", + std_program="python std.py", + input=io, + grader="NOIPStyle", + ) result = out.getvalue().strip() - correct_out = 'python correct.py: Correct' + correct_out = "python correct.py: Correct" self.assertEqual(result, correct_out) def test_concurrent(self): - programs = ['test{}.py'.format(i) for i in range(16)] + programs = ["test{}.py".format(i) for i in range(16)] for fn in programs: - with open(fn, 'w') as f: - f.write('print({})'.format(16)) - with open('std.py', 'w') as f: - f.write('print({})'.format(16)) + with open(fn, "w") as f: + f.write("print({})".format(16)) + with open("std.py", "w") as f: + f.write("print({})".format(16)) with IO() as test: - Compare.program(*[(sys.executable, program) for program in programs], std_program=(sys.executable, 'std.py'), max_workers=None, input=test) + Compare.program( + *[(sys.executable, program) for program in programs], + std_program=(sys.executable, "std.py"), + max_workers=None, + input=test + ) ios = [IO() for i in range(16)] try: for f in ios: - f.output_write('16') + f.output_write("16") with IO() as std: - std.output_write('16') + std.output_write("16") Compare.output(*ios, std=std, max_workers=None) finally: for io in ios: @@ -137,7 +163,11 @@ def test_timeout(self): if sys.version_info >= (3, 3): with IO() as test: try: - Compare.program(((sys.executable, '-c', '__import__(\'time\').sleep(10)'), 1), std=test, input=test) + Compare.program( + ((sys.executable, "-c", "__import__('time').sleep(10)"), 1), + std=test, + input=test, + ) except subprocess.TimeoutExpired: pass else: diff --git a/cyaron/tests/graph_test.py b/cyaron/tests/graph_test.py index 976930a..57b3e07 100644 --- a/cyaron/tests/graph_test.py +++ b/cyaron/tests/graph_test.py @@ -24,14 +24,14 @@ def test_same(self, l, r): def tarjan(graph, n): def new_array(len, val=0): - return [val for _ in range(len+1)] + return [val for _ in range(len + 1)] instack = new_array(n, False) low = new_array(n) dfn = new_array(n, 0) stap = new_array(n) belong = new_array(n) - var = [0, 0, 0] # cnt, bc, stop + var = [0, 0, 0] # cnt, bc, stop # cnt = bc = stop = 0 def dfs(cur): @@ -49,7 +49,7 @@ def dfs(cur): low[cur] = min(low[cur], dfn[v.end]) if dfn[cur] == low[cur]: - v = cur + 1 # set v != cur + v = cur + 1 # set v != cur var[1] += 1 while v != cur: var[2] -= 1 @@ -58,8 +58,8 @@ def dfs(cur): belong[v] = var[1] for i in range(n): - if dfn[i+1] == 0: - dfs(i+1) + if dfn[i + 1] == 0: + dfs(i + 1) return belong @@ -69,20 +69,20 @@ class TestGraph(unittest.TestCase): def test_self_loop(self): graph_size = 20 for _ in range(20): - graph = Graph.graph(graph_size, int(graph_size*2), self_loop=True) + graph = Graph.graph(graph_size, int(graph_size * 2), self_loop=True) has_self_loop = max([e.start == e.end for e in graph.iterate_edges()]) if has_self_loop: break self.assertTrue(has_self_loop) for _ in range(10): - graph = Graph.graph(graph_size, int(graph_size*2), self_loop=False) + graph = Graph.graph(graph_size, int(graph_size * 2), self_loop=False) self.assertFalse(max([e.start == e.end for e in graph.iterate_edges()])) def test_repeated_edges(self): graph_size = 20 for _ in range(20): - graph = Graph.graph(graph_size, int(graph_size*2), repeated_edges=True) + graph = Graph.graph(graph_size, int(graph_size * 2), repeated_edges=True) edges = [(e.start, e.end) for e in graph.iterate_edges()] has_repeated_edges = len(edges) > len(set(edges)) if has_repeated_edges: @@ -90,7 +90,7 @@ def test_repeated_edges(self): self.assertTrue(has_repeated_edges) for _ in range(10): - graph = Graph.graph(graph_size, int(graph_size*2), repeated_edges=False) + graph = Graph.graph(graph_size, int(graph_size * 2), repeated_edges=False) edges = list(graph.iterate_edges()) self.assertEqual(len(edges), len(set(edges))) @@ -101,53 +101,69 @@ def test_tree_connected(self): tree = Graph.tree(graph_size) for edge in tree.iterate_edges(): ufs.merge(edge.start, edge.end) - for i in range(graph_size-1): - self.assertTrue(ufs.test_same(i+1, i+2)) - + for i in range(graph_size - 1): + self.assertTrue(ufs.test_same(i + 1, i + 2)) def test_DAG(self): graph_size = 20 - for _ in range(10): # test 10 times + for _ in range(10): # test 10 times ufs = UnionFindSet(graph_size) - graph = Graph.DAG(graph_size, int(graph_size*1.6), repeated_edges=False, self_loop=False, loop=True) + graph = Graph.DAG( + graph_size, + int(graph_size * 1.6), + repeated_edges=False, + self_loop=False, + loop=True, + ) - self.assertEqual(len(list(graph.iterate_edges())), int(graph_size*1.6)) + self.assertEqual(len(list(graph.iterate_edges())), int(graph_size * 1.6)) for edge in graph.iterate_edges(): ufs.merge(edge.start, edge.end) - for i in range(graph_size-1): - self.assertTrue(ufs.test_same(i+1, i+2)) + for i in range(graph_size - 1): + self.assertTrue(ufs.test_same(i + 1, i + 2)) def test_DAG_without_loop(self): graph_size = 20 - for _ in range(10): # test 10 times + for _ in range(10): # test 10 times ufs = UnionFindSet(graph_size) - graph = Graph.DAG(graph_size, int(graph_size*1.6), repeated_edges=False, self_loop=False, loop=False) + graph = Graph.DAG( + graph_size, + int(graph_size * 1.6), + repeated_edges=False, + self_loop=False, + loop=False, + ) - self.assertEqual(len(list(graph.iterate_edges())), int(graph_size*1.6)) + self.assertEqual(len(list(graph.iterate_edges())), int(graph_size * 1.6)) for edge in graph.iterate_edges(): ufs.merge(edge.start, edge.end) - for i in range(graph_size-1): - self.assertTrue(ufs.test_same(i+1, i+2)) + for i in range(graph_size - 1): + self.assertTrue(ufs.test_same(i + 1, i + 2)) belong = tarjan(graph, graph_size) self.assertEqual(max(belong), graph_size) def test_undirected_graph(self): graph_size = 20 - for _ in range(10): # test 10 times + for _ in range(10): # test 10 times ufs = UnionFindSet(graph_size) - graph = Graph.UDAG(graph_size, int(graph_size*1.6), repeated_edges=False, self_loop=False) + graph = Graph.UDAG( + graph_size, int(graph_size * 1.6), repeated_edges=False, self_loop=False + ) - self.assertEqual(len(list(graph.iterate_edges())), int(graph_size*1.6)) + self.assertEqual(len(list(graph.iterate_edges())), int(graph_size * 1.6)) for edge in graph.iterate_edges(): ufs.merge(edge.start, edge.end) - for i in range(graph_size-1): - self.assertTrue(ufs.test_same(i+1, i+2)) + for i in range(graph_size - 1): + self.assertTrue(ufs.test_same(i + 1, i + 2)) def test_DAG_boundary(self): - with self.assertRaises(Exception, msg="the number of edges of connected graph must more than the number of nodes - 1"): + with self.assertRaises( + Exception, + msg="the number of edges of connected graph must more than the number of nodes - 1", + ): Graph.DAG(8, 6) Graph.DAG(8, 7) diff --git a/cyaron/tests/io_test.py b/cyaron/tests/io_test.py index 1da50e4..e8d88a2 100644 --- a/cyaron/tests/io_test.py +++ b/cyaron/tests/io_test.py @@ -26,7 +26,12 @@ def test_create_files_simple(self): def test_create_files_prefix_id(self): with captured_output() as (out, err): - IO(file_prefix="test_prefix", data_id=233, input_suffix=".inp", output_suffix=".ans") + IO( + file_prefix="test_prefix", + data_id=233, + input_suffix=".inp", + output_suffix=".ans", + ) self.assertTrue(os.path.exists("test_prefix233.inp")) self.assertTrue(os.path.exists("test_prefix233.ans")) @@ -50,8 +55,8 @@ def test_write_stuff(self): input = f.read() with open("test_write.out") as f: output = f.read() - self.assertEqual(input.split(), ['1', '2', '3', '4', '5', '6', '7', '8', '9']) - self.assertEqual(output.split(), ['9', '8', '7', '6', '5', '4', '3', '2', '1']) + self.assertEqual(input.split(), ["1", "2", "3", "4", "5", "6", "7", "8", "9"]) + self.assertEqual(output.split(), ["9", "8", "7", "6", "5", "4", "3", "2", "1"]) self.assertEqual(input.count("\n"), 2) self.assertEqual(output.count("\n"), 2) @@ -64,15 +69,18 @@ def test_output_gen(self): output = f.read() self.assertEqual(output.strip("\n"), "233") + def test_output_gen_limits(self): + + def test_init_overload(self): - with IO(file_prefix='data{', data_id=5) as test: - self.assertEqual(test.input_filename, 'data{5.in') - self.assertEqual(test.output_filename, 'data{5.out') - with IO('data{}.in', 'data{}.out', 5) as test: - self.assertEqual(test.input_filename, 'data5.in') - self.assertEqual(test.output_filename, 'data5.out') - with open('data5.in', 'w+') as fin: - with open('data5.out', 'w+') as fout: + with IO(file_prefix="data{", data_id=5) as test: + self.assertEqual(test.input_filename, "data{5.in") + self.assertEqual(test.output_filename, "data{5.out") + with IO("data{}.in", "data{}.out", 5) as test: + self.assertEqual(test.input_filename, "data5.in") + self.assertEqual(test.output_filename, "data5.out") + with open("data5.in", "w+") as fin: + with open("data5.out", "w+") as fout: with IO(fin, fout) as test: self.assertEqual(test.input_file, fin) self.assertEqual(test.output_file, fout) diff --git a/cyaron/tests/polygon_test.py b/cyaron/tests/polygon_test.py index 7548f3d..cac0ada 100644 --- a/cyaron/tests/polygon_test.py +++ b/cyaron/tests/polygon_test.py @@ -4,7 +4,9 @@ class TestPolygon(unittest.TestCase): def test_convex_hull(self): - hull = Polygon.convex_hull(300, fx=lambda x: int(x * 100000), fy=lambda x: int(x * 100000)) + hull = Polygon.convex_hull( + 300, fx=lambda x: int(x * 100000), fy=lambda x: int(x * 100000) + ) points = hull.points points = sorted(points) # unique @@ -19,8 +21,7 @@ def test_convex_hull(self): a = st[len(st) - 1] b = points[i] o = st[len(st) - 2] - if (a[0] - o[0]) * (b[1] - o[1]) - \ - (a[1] - o[1]) * (b[0] - o[0]) >= 0: + if (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) >= 0: break st.pop() st.append(points[i]) @@ -30,8 +31,7 @@ def test_convex_hull(self): a = st[len(st) - 1] b = points[i] o = st[len(st) - 2] - if (a[0] - o[0]) * (b[1] - o[1]) - \ - (a[1] - o[1]) * (b[0] - o[0]) >= 0: + if (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) >= 0: break st.pop() st.append(points[i]) @@ -55,8 +55,10 @@ def test_simple_polygon(self): c = points[j] d = points[(j + 1) % len(points)] prod = lambda x, y: x[0] * y[1] - x[1] * y[0] - t1 = prod([c[0] - a[0], c[1] - a[1]], [d[0] - a[0], d[1] - a[1]]) \ - * prod([c[0] - b[0], c[1] - b[1]], [d[0] - b[0], d[1] - b[1]]) - t2 = prod([a[0] - c[0], a[1] - c[1]], [b[0] - c[0], b[1] - c[1]]) \ - * prod([a[0] - d[0], a[1] - d[1]], [b[0] - d[0], b[1] - d[1]]) + t1 = prod( + [c[0] - a[0], c[1] - a[1]], [d[0] - a[0], d[1] - a[1]] + ) * prod([c[0] - b[0], c[1] - b[1]], [d[0] - b[0], d[1] - b[1]]) + t2 = prod( + [a[0] - c[0], a[1] - c[1]], [b[0] - c[0], b[1] - c[1]] + ) * prod([a[0] - d[0], a[1] - d[1]], [b[0] - d[0], b[1] - d[1]]) self.assertFalse(t1 <= 1e-9 and t2 <= 1e-9) diff --git a/cyaron/tests/sequence_test.py b/cyaron/tests/sequence_test.py index 272a1d5..035fcb3 100644 --- a/cyaron/tests/sequence_test.py +++ b/cyaron/tests/sequence_test.py @@ -20,4 +20,3 @@ def test_func_get_one(self): def test_func_get_many(self): seq = Sequence(lambda i, f: 3 * i + 2 * f(i - 1), [0]) self.assertEqual(seq.get(3, 5), [33, 78, 171]) - diff --git a/cyaron/tests/str_test.py b/cyaron/tests/str_test.py index 3f12fab..517a7ef 100644 --- a/cyaron/tests/str_test.py +++ b/cyaron/tests/str_test.py @@ -25,6 +25,6 @@ def test_random_paragraph(self): String.random_paragraph(10) def test_random_regular(self): - pattern = r'[0-9]+\w_.{0,9}' + pattern = r"[0-9]+\w_.{0,9}" sentence = String.random_regular(pattern, limit=5) - self.assertTrue(re.match(pattern, sentence).group() == sentence) \ No newline at end of file + self.assertTrue(re.match(pattern, sentence).group() == sentence) diff --git a/cyaron/utils.py b/cyaron/utils.py index 4ef7358..11a438e 100644 --- a/cyaron/utils.py +++ b/cyaron/utils.py @@ -1,14 +1,14 @@ def ati(array): """ati(array) -> list - Convert all the elements in the array and return them in a list. + Convert all the elements in the array and return them in a list. """ return [int(i) for i in array] def list_like(data): """list_like(data) -> bool - Judge whether the object data is like a list or a tuple. - object data -> the data to judge + Judge whether the object data is like a list or a tuple. + object data -> the data to judge """ return isinstance(data, tuple) or isinstance(data, list) @@ -24,7 +24,7 @@ def int_like(data): def strtolines(str): - lines = str.split('\n') + lines = str.split("\n") for i in range(len(lines)): lines[i] = lines[i].rstrip() @@ -39,6 +39,7 @@ def make_unicode(data): except NameError: return str(data) + def unpack_kwargs(funcname, kwargs, arg_pattern): rv = {} kwargs = kwargs.copy() @@ -58,7 +59,15 @@ def unpack_kwargs(funcname, kwargs, arg_pattern): except KeyError as e: error = True if error: - raise TypeError('{}() missing 1 required keyword-only argument: \'{}\''.format(funcname, tp)) + raise TypeError( + "{}() missing 1 required keyword-only argument: '{}'".format( + funcname, tp + ) + ) if kwargs: - raise TypeError('{}() got an unexpected keyword argument \'{}\''.format(funcname, next(iter(kwargs.items()))[0])) + raise TypeError( + "{}() got an unexpected keyword argument '{}'".format( + funcname, next(iter(kwargs.items()))[0] + ) + ) return rv diff --git a/cyaron/vector.py b/cyaron/vector.py index 2620c59..3016883 100644 --- a/cyaron/vector.py +++ b/cyaron/vector.py @@ -14,7 +14,9 @@ class VectorRandomMode(Enum): class Vector: @staticmethod - def random(num: int = 5, position_range: list = None, mode: VectorRandomMode = 0, **kwargs): + def random( + num: int = 5, position_range: list = None, mode: VectorRandomMode = 0, **kwargs + ): """ brief : generating n random vectors in limited space param : @@ -47,16 +49,24 @@ def random(num: int = 5, position_range: list = None, mode: VectorRandomMode = 0 else: offset.append(0) length.append(position_range[i]) - vector_space *= (length[i] + 1) + vector_space *= length[i] + 1 if mode == VectorRandomMode.unique and num > vector_space: - raise Exception("1st param is so large that CYaRon can not generate unique vectors") + raise Exception( + "1st param is so large that CYaRon can not generate unique vectors" + ) result = [] if mode == VectorRandomMode.repeatable: - result = [[random.randint(x, y) for x, y in zip(offset, length)] for _ in range(num)] + result = [ + [random.randint(x, y) for x, y in zip(offset, length)] + for _ in range(num) + ] elif mode == VectorRandomMode.float: - result = [[random.uniform(x, y) for x, y in zip(offset, length)] for _ in range(num)] + result = [ + [random.uniform(x, y) for x, y in zip(offset, length)] + for _ in range(num) + ] elif mode == VectorRandomMode.unique and vector_space > 5 * num: # O(NlogN) num_set = set() @@ -87,5 +97,5 @@ def get_vector(dimension: int, position_range: list, hashcode: int): tmp = [] for i in range(0, dimension): tmp.append(hashcode % (position_range[i] + 1)) - hashcode //= (position_range[i] + 1) + hashcode //= position_range[i] + 1 return tmp diff --git a/cyaron/visual.py b/cyaron/visual.py index 7b5f122..3e0f63d 100644 --- a/cyaron/visual.py +++ b/cyaron/visual.py @@ -1,26 +1,26 @@ -from .graph import * +from .graph import * from .merger import Merger import pygraphviz as pgv + def visualize(graph, output_path="a.png"): """visualize(graph, **kwargs) -> None - Graph/Merger graph -> the graph/Merger that will be visualized - string output_path-> the output path of the image + Graph/Merger graph -> the graph/Merger that will be visualized + string output_path-> the output path of the image """ - if isinstance(graph, Merger): graph = Merger.G + if isinstance(graph, Merger): + graph = Merger.G G = pgv.AGraph(directed=graph.directed) G.add_nodes_from([i for i in xrange(1, len(graph.edges))]) for edge in graph.iterate_edges(): G.add_edge(edge.start, edge.end, label=edge.weight) - - G.node_attr['shape'] = 'egg' - G.node_attr['width'] = '0.25' - G.node_attr['height'] = '0.25' - G.edge_attr['arrowhead'] = 'open' - G.layout(prog='dot') - G.draw(output_path) - + G.node_attr["shape"] = "egg" + G.node_attr["width"] = "0.25" + G.node_attr["height"] = "0.25" + G.edge_attr["arrowhead"] = "open" + G.layout(prog="dot") + G.draw(output_path) From 0121c5933923099163fe48756172c21baa2f5c71 Mon Sep 17 00:00:00 2001 From: FredB-mine Date: Thu, 7 Mar 2024 20:46:13 +0800 Subject: [PATCH 2/4] Added time limit for io --- cyaron/compare.py | 135 +++--------- cyaron/consts.py | 4 +- cyaron/graders/fulltext.py | 12 +- cyaron/graders/graderregistry.py | 2 +- cyaron/graders/mismatch.py | 25 +-- cyaron/graders/noipstyle.py | 44 ++-- cyaron/graph.py | 312 +++++++++++++--------------- cyaron/io.py | 31 ++- cyaron/log.py | 82 +++----- cyaron/math.py | 342 ++++++++++++------------------- cyaron/merger.py | 52 +++-- cyaron/output_capture.py | 4 +- cyaron/polygon.py | 73 +++---- cyaron/sequence.py | 10 +- cyaron/string.py | 13 +- cyaron/tests/compare_test.py | 76 +++---- cyaron/tests/graph_test.py | 72 +++---- cyaron/tests/io_test.py | 31 ++- cyaron/tests/polygon_test.py | 20 +- cyaron/tests/sequence_test.py | 1 + cyaron/tests/str_test.py | 4 +- cyaron/utils.py | 21 +- cyaron/vector.py | 22 +- cyaron/visual.py | 24 +-- 24 files changed, 560 insertions(+), 852 deletions(-) diff --git a/cyaron/compare.py b/cyaron/compare.py index 66f76a7..ad613a0 100644 --- a/cyaron/compare.py +++ b/cyaron/compare.py @@ -17,7 +17,7 @@ def __init__(self, name, mismatch): self.mismatch = mismatch def __str__(self): - return "In program: '{}'. {}".format(self.name, self.mismatch) + return 'In program: \'{}\'. {}'.format(self.name,self.mismatch) class Compare: @@ -37,7 +37,7 @@ def __process_file(file): file.output_file.seek(0) return file.output_filename, file.output_file.read() else: - with open(file, "r", newline="\n") as f: + with open(file, "r", newline='\n') as f: return file, f.read() @staticmethod @@ -50,43 +50,26 @@ def __normal_max_workers(workers): @classmethod def output(cls, *files, **kwargs): - kwargs = unpack_kwargs( - "output", - kwargs, - ( - "std", - ("grader", DEFAULT_GRADER), - ("max_workers", -1), - ("job_pool", None), - ("stop_on_incorrect", None), - ), - ) - std = kwargs["std"] - grader = kwargs["grader"] - max_workers = kwargs["max_workers"] - job_pool = kwargs["job_pool"] - if kwargs["stop_on_incorrect"] is not None: + kwargs = unpack_kwargs('output', kwargs, ('std', ('grader', DEFAULT_GRADER), ('max_workers', -1), + ('job_pool', None), ('stop_on_incorrect', None))) + std = kwargs['std'] + grader = kwargs['grader'] + max_workers = kwargs['max_workers'] + job_pool = kwargs['job_pool'] + if kwargs['stop_on_incorrect'] is not None: log.warn("parameter stop_on_incorrect is deprecated and has no effect.") if (max_workers is None or max_workers >= 0) and job_pool is None: max_workers = cls.__normal_max_workers(max_workers) try: from concurrent.futures import ThreadPoolExecutor - with ThreadPoolExecutor(max_workers=max_workers) as job_pool: - return cls.output( - *files, - std=std, - grader=grader, - max_workers=max_workers, - job_pool=job_pool - ) + return cls.output(*files, std=std, grader=grader, max_workers=max_workers, job_pool=job_pool) except ImportError: pass def get_std(): return cls.__process_file(std)[1] - if job_pool is not None: std = job_pool.submit(get_std).result() else: @@ -103,121 +86,61 @@ def do(file): @classmethod def program(cls, *programs, **kwargs): - kwargs = unpack_kwargs( - "program", - kwargs, - ( - "input", - ("std", None), - ("std_program", None), - ("grader", DEFAULT_GRADER), - ("max_workers", -1), - ("job_pool", None), - ("stop_on_incorrect", None), - ), - ) - input = kwargs["input"] - std = kwargs["std"] - std_program = kwargs["std_program"] - grader = kwargs["grader"] - max_workers = kwargs["max_workers"] - job_pool = kwargs["job_pool"] - time_limit = kwargs["time_limit"] - memory_limit = kwargs["memory_limit"] - - if kwargs["stop_on_incorrect"] is not None: + kwargs = unpack_kwargs('program', kwargs, ('input', ('std', None), ('std_program', None), + ('grader', DEFAULT_GRADER), ('max_workers', -1), + ('job_pool', None), ('stop_on_incorrect', None))) + input = kwargs['input'] + std = kwargs['std'] + std_program = kwargs['std_program'] + grader = kwargs['grader'] + max_workers = kwargs['max_workers'] + job_pool = kwargs['job_pool'] + if kwargs['stop_on_incorrect'] is not None: log.warn("parameter stop_on_incorrect is deprecated and has no effect.") if (max_workers is None or max_workers >= 0) and job_pool is None: max_workers = cls.__normal_max_workers(max_workers) try: from concurrent.futures import ThreadPoolExecutor - with ThreadPoolExecutor(max_workers=max_workers) as job_pool: - return cls.program( - *programs, - input=input, - std=std, - std_program=std_program, - grader=grader, - max_workers=max_workers, - job_pool=job_pool - ) + return cls.program(*programs, input=input, std=std, std_program=std_program, grader=grader, max_workers=max_workers, job_pool=job_pool) except ImportError: pass if not isinstance(input, IO): - raise TypeError( - "expect {}, got {}".format(type(IO).__name__, type(input).__name__) - ) + raise TypeError("expect {}, got {}".format(type(IO).__name__, type(input).__name__)) input.flush_buffer() input.input_file.seek(0) if std_program is not None: - def get_std(): - with open( - os.dup(input.input_file.fileno()), "r", newline="\n" - ) as input_file: - content = make_unicode( - subprocess.check_output( - std_program, - shell=(not list_like(std_program)), - stdin=input.input_file, - universal_newlines=True, - ) - ) + with open(os.dup(input.input_file.fileno()), 'r', newline='\n') as input_file: + content = make_unicode(subprocess.check_output(std_program, shell=(not list_like(std_program)), stdin=input.input_file, universal_newlines=True)) input_file.seek(0) return content - if job_pool is not None: std = job_pool.submit(get_std).result() else: std = get_std() elif std is not None: - def get_std(): return cls.__process_file(std)[1] - if job_pool is not None: std = job_pool.submit(get_std).result() else: std = get_std() else: - raise TypeError( - "program() missing 1 required non-None keyword-only argument: 'std' or 'std_program'" - ) + raise TypeError('program() missing 1 required non-None keyword-only argument: \'std\' or \'std_program\'') def do(program_name): timeout = None - if ( - list_like(program_name) - and len(program_name) == 2 - and int_like(program_name[-1]) - ): + if list_like(program_name) and len(program_name) == 2 and int_like(program_name[-1]): program_name, timeout = program_name - with open( - os.dup(input.input_file.fileno()), "r", newline="\n" - ) as input_file: + with open(os.dup(input.input_file.fileno()), 'r', newline='\n') as input_file: if timeout is None: - content = make_unicode( - subprocess.check_output( - program_name, - shell=(not list_like(program_name)), - stdin=input_file, - universal_newlines=True, - ) - ) + content = make_unicode(subprocess.check_output(program_name, shell=(not list_like(program_name)), stdin=input_file, universal_newlines=True)) else: - content = make_unicode( - subprocess.check_output( - program_name, - shell=(not list_like(program_name)), - stdin=input_file, - universal_newlines=True, - timeout=timeout, - ) - ) + content = make_unicode(subprocess.check_output(program_name, shell=(not list_like(program_name)), stdin=input_file, universal_newlines=True, timeout=timeout)) input_file.seek(0) cls.__compare_two(program_name, content, std, grader) diff --git a/cyaron/consts.py b/cyaron/consts.py index e6fab00..c838ef3 100644 --- a/cyaron/consts.py +++ b/cyaron/consts.py @@ -18,7 +18,7 @@ ALPHABET_CAPITAL = string.ascii_uppercase ALPHABET = ALPHABET_SMALL + ALPHABET_CAPITAL NUMBERS = string.digits -SENTENCE_SEPARATORS = ",,,,,,,;;:" # 70% ',' 20% ';' 10% ':' -SENTENCE_TERMINATORS = "....!" # 80% '.' 20% '!' +SENTENCE_SEPARATORS = ',,,,,,,;;:' # 70% ',' 20% ';' 10% ':' +SENTENCE_TERMINATORS = '....!' # 80% '.' 20% '!' DEFAULT_GRADER = "NOIPStyle" diff --git a/cyaron/graders/fulltext.py b/cyaron/graders/fulltext.py index b5dbb07..8460b6f 100644 --- a/cyaron/graders/fulltext.py +++ b/cyaron/graders/fulltext.py @@ -2,13 +2,9 @@ from .graderregistry import CYaRonGraders from .mismatch import HashMismatch - @CYaRonGraders.grader("FullText") def fulltext(content, std): - content_hash = hashlib.sha256(content.encode("utf-8")).hexdigest() - std_hash = hashlib.sha256(std.encode("utf-8")).hexdigest() - return ( - (True, None) - if content_hash == std_hash - else (False, HashMismatch(content, std, content_hash, std_hash)) - ) + content_hash = hashlib.sha256(content.encode('utf-8')).hexdigest() + std_hash = hashlib.sha256(std.encode('utf-8')).hexdigest() + return (True, None) if content_hash == std_hash else (False, HashMismatch(content, std, content_hash, std_hash)) + diff --git a/cyaron/graders/graderregistry.py b/cyaron/graders/graderregistry.py index 702e39d..2fd1419 100644 --- a/cyaron/graders/graderregistry.py +++ b/cyaron/graders/graderregistry.py @@ -15,4 +15,4 @@ def check(self, name): return name in self._registry -CYaRonGraders = GraderRegistry() +CYaRonGraders = GraderRegistry() \ No newline at end of file diff --git a/cyaron/graders/mismatch.py b/cyaron/graders/mismatch.py index 7c8eb21..70c2dfc 100644 --- a/cyaron/graders/mismatch.py +++ b/cyaron/graders/mismatch.py @@ -1,6 +1,5 @@ class Mismatch(ValueError): """exception for content mismatch""" - def __init__(self, content, std, *args): """ content -> content got @@ -10,10 +9,8 @@ def __init__(self, content, std, *args): self.content = content self.std = std - class HashMismatch(Mismatch): """exception for hash mismatch""" - def __init__(self, content, std, content_hash, std_hash): """ content -> content got @@ -26,25 +23,11 @@ def __init__(self, content, std, content_hash, std_hash): self.std_hash = std_hash def __str__(self): - return "Hash mismatch: read %s, expected %s" % ( - self.content_hash, - self.std_hash, - ) - + return "Hash mismatch: read %s, expected %s" % (self.content_hash, self.std_hash) class TextMismatch(Mismatch): """exception for text mismatch""" - - def __init__( - self, - content, - std, - err_msg, - lineno=None, - colno=None, - content_token=None, - std_token=None, - ): + def __init__(self, content, std, err_msg, lineno=None, colno=None, content_token=None, std_token=None): """ content -> content got std -> content expected @@ -54,9 +37,7 @@ def __init__( content_token -> the token of content mismatch std_token -> the token of std """ - super(TextMismatch, self).__init__( - content, std, err_msg, lineno, colno, content_token, std_token - ) + super(TextMismatch, self).__init__(content, std, err_msg, lineno, colno, content_token, std_token) self.err_msg = err_msg.format(lineno, colno, content_token, std_token) self.lineno = lineno self.colno = colno diff --git a/cyaron/graders/noipstyle.py b/cyaron/graders/noipstyle.py index 679e059..bf6fa21 100644 --- a/cyaron/graders/noipstyle.py +++ b/cyaron/graders/noipstyle.py @@ -5,46 +5,28 @@ @CYaRonGraders.grader("NOIPStyle") def noipstyle(content, std): - content_lines = strtolines(content.replace("\r\n", "\n")) - std_lines = strtolines(std.replace("\r\n", "\n")) + content_lines = strtolines(content.replace('\r\n', '\n')) + std_lines = strtolines(std.replace('\r\n', '\n')) if len(content_lines) != len(std_lines): - return False, TextMismatch(content, std, "Too many or too few lines.") + return False, TextMismatch(content, std, 'Too many or too few lines.') for i in range(len(content_lines)): if std_lines[i] != content_lines[i]: for j in range(min(len(std_lines[i]), len(content_lines[i]))): if std_lines[i][j] != content_lines[i][j]: - return ( - False, - TextMismatch( - content, - std, - "On line {} column {}, read {}, expected {}.", - i + 1, - j + 1, - content_lines[i][j : j + 5], - std_lines[i][j : j + 5], - ), - ) + return (False, + TextMismatch( + content, std, + 'On line {} column {}, read {}, expected {}.', + i + 1, j + 1, content_lines[i][j:j + 5], + std_lines[i][j:j + 5])) if len(std_lines[i]) > len(content_lines[i]): return False, TextMismatch( - content, - std, - "Too short on line {}.", - i + 1, - j + 1, - content_lines[i][j : j + 5], - std_lines[i][j : j + 5], - ) + content, std, 'Too short on line {}.', i + 1, j + 1, + content_lines[i][j:j + 5], std_lines[i][j:j + 5]) if len(std_lines[i]) < len(content_lines[i]): return False, TextMismatch( - content, - std, - "Too long on line {}.", - i + 1, - j + 1, - content_lines[i][j : j + 5], - std_lines[i][j : j + 5], - ) + content, std, 'Too long on line {}.', i + 1, j + 1, + content_lines[i][j:j + 5], std_lines[i][j:j + 5]) return True, None diff --git a/cyaron/graph.py b/cyaron/graph.py index c95e8b5..34add04 100644 --- a/cyaron/graph.py +++ b/cyaron/graph.py @@ -4,13 +4,12 @@ class Edge: """Class Edge: A class of the edge in the graph""" - def __init__(self, u, v, w): """__init__(self, u, v, w) -> None - Initialize a edge. - int u -> the start vertex - int v -> the end vertex - int w -> the weight. + Initialize a edge. + int u -> the start vertex + int v -> the end vertex + int w -> the weight. """ self.start = u self.end = v @@ -18,36 +17,35 @@ def __init__(self, u, v, w): def __str__(self): """__str__(self) -> str - Return a string to output the edge. The string contains the start vertex, end vertex and weight(u,v,w) and splits with space. + Return a string to output the edge. The string contains the start vertex, end vertex and weight(u,v,w) and splits with space. """ return "%d %d %d" % (self.start, self.end, self.weight) @staticmethod def unweighted_edge(edge): """unweighted_edge(edge) -> str - Return a string to output the edge without weight. The string contains the start vertex, end vertex(u,v) and splits with space. + Return a string to output the edge without weight. The string contains the start vertex, end vertex(u,v) and splits with space. """ - return "%d %d" % (edge.start, edge.end) - + return '%d %d'%(edge.start,edge.end) class Graph: - """Class Graph: A class of the graph""" - + """Class Graph: A class of the graph + """ def __init__(self, point_count, directed=False): """__init__(self, point_count) -> None - Initialize a graph. - int point_count -> the count of the vertexes in the graph. - bool directed = False -> whether the graph is directed(true:directed,false:not directed) + Initialize a graph. + int point_count -> the count of the vertexes in the graph. + bool directed = False -> whether the graph is directed(true:directed,false:not directed) """ self.directed = directed self.edges = [[] for i in range(point_count + 1)] def to_str(self, **kwargs): """to_str(self, **kwargs) -> str - Convert the graph to string with format. Splits with "\n" - **kwargs(Keyword args): - bool shuffle = False -> whether shuffle the output or not - str output(Edge) = str -> the convert function which converts object Edge to str. the default way is to use str() + Convert the graph to string with format. Splits with "\n" + **kwargs(Keyword args): + bool shuffle = False -> whether shuffle the output or not + str output(Edge) = str -> the convert function which converts object Edge to str. the default way is to use str() """ shuffle = kwargs.get("shuffle", False) output = kwargs.get("output", str) @@ -59,8 +57,7 @@ def to_str(self, **kwargs): edge_buf = [] for edge in self.iterate_edges(): edge_buf.append( - Edge(new_node_id[edge.start], new_node_id[edge.end], edge.weight) - ) + Edge(new_node_id[edge.start], new_node_id[edge.end], edge.weight)) random.shuffle(edge_buf) for edge in edge_buf: if not self.directed and random.randint(0, 1) == 0: @@ -73,13 +70,13 @@ def to_str(self, **kwargs): def __str__(self): """__str__(self) -> str - Return a string to output the graph. The string contains all the edges of the graph, splits with "\n". + Return a string to output the graph. The string contains all the edges of the graph, splits with "\n". """ return self.to_str() def iterate_edges(self): """iterate_edges(self) -> Edge - Iter the graph. Order by the start vertex. + Iter the graph. Order by the start vertex. """ for node in self.edges: for edge in node: @@ -88,16 +85,16 @@ def iterate_edges(self): def __add_edge(self, x, y, w): """__add_edge(self, x, y, w) -> None - Add an edge to the graph. + Add an edge to the graph. """ self.edges[x].append(Edge(x, y, w)) def add_edge(self, x, y, **kwargs): """add_edge(self, x, y, **kwargs) -> None - int x -> the start vertex - int y -> the end vertex - **kwargs(Keyword args): - int weight = 1 -> the weight + int x -> the start vertex + int y -> the end vertex + **kwargs(Keyword args): + int weight = 1 -> the weight """ weight = kwargs.get("weight", 1) self.__add_edge(x, y, weight) @@ -107,56 +104,56 @@ def add_edge(self, x, y, **kwargs): @staticmethod def chain(point_count, **kwargs): """chain(point_count, **kwargs) -> Graph - Factory method. Return a chain graph with point_count vertexes. - int point_count -> the count of vertexes - **kwargs(Keyword args): - bool directed = True -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a chain graph with point_count vertexes. + int point_count -> the count of vertexes + **kwargs(Keyword args): + bool directed = True -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ return Graph.tree(point_count, 1, 0, **kwargs) @staticmethod def flower(point_count, **kwargs): """flower(point_count, **kwargs) -> Graph - Factory method. Return a flower graph with point_count vertexes. - int point_count -> the count of vertexes - **kwargs(Keyword args): - bool directed = True -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a flower graph with point_count vertexes. + int point_count -> the count of vertexes + **kwargs(Keyword args): + bool directed = True -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ return Graph.tree(point_count, 0, 1, **kwargs) @staticmethod def tree(point_count, chain=0, flower=0, **kwargs): """tree(point_count, chain=0, flower=0, **kwargs) -> Graph - Factory method. Return a tree with point_count vertexes. - int point_count -> the count of vertexes - float chain = 0 -> 1 means the tree is a chain - float flower = 0 -> 1 means the tree is a flower - NOTICE:only either chain or flower can be True - **kwargs(Keyword args): - bool directed = False -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a tree with point_count vertexes. + int point_count -> the count of vertexes + float chain = 0 -> 1 means the tree is a chain + float flower = 0 -> 1 means the tree is a flower + NOTICE:only either chain or flower can be True + **kwargs(Keyword args): + bool directed = False -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ directed = kwargs.get("directed", False) weight_limit = kwargs.get("weight_limit", (1, 1)) if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) - ) + "weight_gen", lambda: random.randint( + weight_limit[0], weight_limit[1])) if not 0 <= chain <= 1 or not 0 <= flower <= 1: raise Exception("chain and flower must be between 0 and 1") @@ -185,51 +182,51 @@ def tree(point_count, chain=0, flower=0, **kwargs): @staticmethod def binary_tree(point_count, left=0, right=0, **kwargs): """binary_tree(point_count, left=0, right=0, **kwargs) -> Graph - Factory method. Return a binary tree with point_count vertexes. - int point_count -> the count of vertexes - float left = 0 -> random arg. should be in [0,1] - float right = 0 -> random arg. should be in [0,1] - NOTICE:left+right mustn't be greater than 1 - **kwargs(Keyword args): - bool directed = False -> whether the binary tree is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a binary tree with point_count vertexes. + int point_count -> the count of vertexes + float left = 0 -> random arg. should be in [0,1] + float right = 0 -> random arg. should be in [0,1] + NOTICE:left+right mustn't be greater than 1 + **kwargs(Keyword args): + bool directed = False -> whether the binary tree is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ directed = kwargs.get("directed", False) weight_limit = kwargs.get("weight_limit", (1, 1)) if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) - ) + "weight_gen", lambda: random.randint( + weight_limit[0], weight_limit[1])) if not 0 <= left <= 1 or not 0 <= right <= 1: raise Exception("left and right must be between 0 and 1") if left + right > 1: raise Exception("left plus right must be smaller than 1") - - can_left = [1] - can_right = [1] + + can_left=[1] + can_right=[1] graph = Graph(point_count, directed) for i in range(2, point_count + 1): edge_pos = random.random() node = 0 # Left if edge_pos < left or left + right < edge_pos <= (1.0 - left - right) / 2: - point_index = random.randint(0, len(can_left) - 1) + point_index = random.randint(0,len(can_left)-1) node = can_left[point_index] - del_last_node = can_left.pop() # Save a copy of the last element + del_last_node = can_left.pop() # Save a copy of the last element if point_index < len(can_left): # If the chosen element isn't the last one, # Copy the last one to the position of the chosen one can_left[point_index] = del_last_node # Right else: - # elif left <= edge_pos <= left + right or (1.0 - left - right) / 2 < edge_pos < 1: - point_index = random.randint(0, len(can_right) - 1) + # elif left <= edge_pos <= left + right or (1.0 - left - right) / 2 < edge_pos < 1: + point_index = random.randint(0,len(can_right)-1) node = can_right[point_index] del_last_node = can_right.pop() if point_index < len(can_right): @@ -243,18 +240,18 @@ def binary_tree(point_count, left=0, right=0, **kwargs): @staticmethod def graph(point_count, edge_count, **kwargs): """graph(point_count, edge_count, **kwargs) -> Graph - Factory method. Return a graph with point_count vertexes and edge_count edges. - int point_count -> the count of vertexes - int edge_count -> the count of edges - **kwargs(Keyword args): - bool self_loop = True -> whether to allow self loops or not - bool repeated_edges = True -> whether to allow repeated edges or not - bool directed = False -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a graph with point_count vertexes and edge_count edges. + int point_count -> the count of vertexes + int edge_count -> the count of edges + **kwargs(Keyword args): + bool self_loop = True -> whether to allow self loops or not + bool repeated_edges = True -> whether to allow repeated edges or not + bool directed = False -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ directed = kwargs.get("directed", False) self_loop = kwargs.get("self_loop", True) @@ -263,8 +260,8 @@ def graph(point_count, edge_count, **kwargs): if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) - ) + "weight_gen", lambda: random.randint( + weight_limit[0], weight_limit[1])) graph = Graph(point_count, directed) used_edges = set() i = 0 @@ -272,9 +269,7 @@ def graph(point_count, edge_count, **kwargs): u = random.randint(1, point_count) v = random.randint(1, point_count) - if (not self_loop and u == v) or ( - not repeated_edges and (u, v) in used_edges - ): + if (not self_loop and u == v) or (not repeated_edges and (u, v) in used_edges): # Then we generate a new pair of nodes continue @@ -291,34 +286,32 @@ def graph(point_count, edge_count, **kwargs): @staticmethod def DAG(point_count, edge_count, **kwargs): """DAG(point_count, edge_count, **kwargs) -> Graph - Factory method. Return a graph with point_count vertexes and edge_count edges. - int point_count -> the count of vertexes - int edge_count -> the count of edges - **kwargs(Keyword args): - bool self_loop = False -> whether to allow self loops or not - bool repeated_edges = True -> whether to allow repeated edges or not - bool loop = False -> whether to allow loops or not - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a graph with point_count vertexes and edge_count edges. + int point_count -> the count of vertexes + int edge_count -> the count of edges + **kwargs(Keyword args): + bool self_loop = False -> whether to allow self loops or not + bool repeated_edges = True -> whether to allow repeated edges or not + bool loop = False -> whether to allow loops or not + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ if edge_count < point_count - 1: - raise Exception( - "the number of edges of connected graph must more than the number of nodes - 1" - ) + raise Exception("the number of edges of connected graph must more than the number of nodes - 1") - self_loop = kwargs.get("self_loop", False) # DAG default has no loop + self_loop = kwargs.get("self_loop", False) # DAG default has no loop repeated_edges = kwargs.get("repeated_edges", True) loop = kwargs.get("loop", False) weight_limit = kwargs.get("weight_limit", (1, 1)) if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) - ) - + "weight_gen", lambda: random.randint( + weight_limit[0], weight_limit[1])) + used_edges = set() edge_buf = list(Graph.tree(point_count, weight_gen=weight_gen).iterate_edges()) graph = Graph(point_count, directed=True) @@ -327,10 +320,10 @@ def DAG(point_count, edge_count, **kwargs): if loop and random.randint(1, 2) == 1: edge.start, edge.end = edge.end, edge.start graph.add_edge(edge.start, edge.end, weight=edge.weight) - + if not repeated_edges: used_edges.add((edge.start, edge.end)) - + i = point_count - 1 while i < edge_count: u = random.randint(1, point_count) @@ -339,9 +332,7 @@ def DAG(point_count, edge_count, **kwargs): if not loop and u > v: u, v = v, u - if (not self_loop and u == v) or ( - not repeated_edges and (u, v) in used_edges - ): + if (not self_loop and u == v) or (not repeated_edges and (u, v) in used_edges): # Then we generate a new pair of nodes continue @@ -357,22 +348,20 @@ def DAG(point_count, edge_count, **kwargs): @staticmethod def UDAG(point_count, edge_count, **kwargs): """UDAG(point_count, edge_count, **kwargs) -> Graph - Factory method. Return a graph with point_count vertexes and edge_count edges. - int point_count -> the count of vertexes - int edge_count -> the count of edges - **kwargs(Keyword args): - bool self_loop = True -> whether to allow self loops or not - bool repeated_edges = True -> whether to allow repeated edges or not - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a graph with point_count vertexes and edge_count edges. + int point_count -> the count of vertexes + int edge_count -> the count of edges + **kwargs(Keyword args): + bool self_loop = True -> whether to allow self loops or not + bool repeated_edges = True -> whether to allow repeated edges or not + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ - if edge_count < point_count - 1: - raise Exception( - "the number of edges of connected graph must more than the number of nodes - 1" - ) + if edge_count < point_count - 1: + raise Exception("the number of edges of connected graph must more than the number of nodes - 1") self_loop = kwargs.get("self_loop", True) repeated_edges = kwargs.get("repeated_edges", True) @@ -380,9 +369,9 @@ def UDAG(point_count, edge_count, **kwargs): if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) - ) - + "weight_gen", lambda: random.randint( + weight_limit[0], weight_limit[1])) + used_edges = set() graph = Graph.tree(point_count, weight_gen=weight_gen, directed=False) @@ -390,15 +379,13 @@ def UDAG(point_count, edge_count, **kwargs): if not repeated_edges: used_edges.add((edge.start, edge.end)) used_edges.add((edge.end, edge.start)) - + i = point_count - 1 while i < edge_count: u = random.randint(1, point_count) v = random.randint(1, point_count) - if (not self_loop and u == v) or ( - not repeated_edges and (u, v) in used_edges - ): + if (not self_loop and u == v) or (not repeated_edges and (u, v) in used_edges): # Then we generate a new pair of nodes continue @@ -415,16 +402,16 @@ def UDAG(point_count, edge_count, **kwargs): @staticmethod def hack_spfa(point_count, **kwargs): """hack_spfa(point_count, **kwargs) -> None - Factory method. Return a spfa graph with point_count vertexes - int point_count -> the count of vertexes - **kwargs(Keyword args): - bool directed = False -> whether the chain is directed(true:directed,false:not directed) - (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) - int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) - int extra_edge = 2 -> the number of extra edges - int/float weight_gen() - = lambda: random.randint(weight_limit[0], weight_limit[1]) - -> the generator of the weights. It should return the weight. The default way is to use the random.randint() + Factory method. Return a spfa graph with point_count vertexes + int point_count -> the count of vertexes + **kwargs(Keyword args): + bool directed = False -> whether the chain is directed(true:directed,false:not directed) + (int,int) weight_limit = (1,1) -> the limit of weight. index 0 is the min limit, and index 1 is the max limit(both included) + int weight_limit -> If you use a int for this arg, it means the max limit of the weight(included) + int extra_edge = 2 -> the number of extra edges + int/float weight_gen() + = lambda: random.randint(weight_limit[0], weight_limit[1]) + -> the generator of the weights. It should return the weight. The default way is to use the random.randint() """ directed = kwargs.get("directed", False) extraedg = kwargs.get("extra_edge", 2) @@ -432,8 +419,8 @@ def hack_spfa(point_count, **kwargs): if not list_like(weight_limit): weight_limit = (1, weight_limit) weight_gen = kwargs.get( - "weight_gen", lambda: random.randint(weight_limit[0], weight_limit[1]) - ) + "weight_gen", lambda: random.randint( + weight_limit[0], weight_limit[1])) point_to_skip = point_count + 3 graph = Graph(point_count, directed) @@ -443,18 +430,15 @@ def hack_spfa(point_count, **kwargs): for i in range(1, half): (x, y) = (i, i + 1) - graph.add_edge( - x + (x >= point_to_skip), y + (y >= point_to_skip), weight=weight_gen() - ) + graph.add_edge(x + (x >= point_to_skip), y + + (y >= point_to_skip), weight=weight_gen()) (x, y) = (i + half, i + half + 1) - graph.add_edge( - x + (x >= point_to_skip), y + (y >= point_to_skip), weight=weight_gen() - ) + graph.add_edge(x + (x >= point_to_skip), y + + (y >= point_to_skip), weight=weight_gen()) for i in range(1, half + 1): (x, y) = (i, i + half) - graph.add_edge( - x + (x >= point_to_skip), y + (y >= point_to_skip), weight=weight_gen() - ) + graph.add_edge(x + (x >= point_to_skip), y + + (y >= point_to_skip), weight=weight_gen()) for i in range(extraedg): u = random.randint(1, point_count) diff --git a/cyaron/io.py b/cyaron/io.py index 2c00ca7..9e8a4bc 100644 --- a/cyaron/io.py +++ b/cyaron/io.py @@ -174,21 +174,32 @@ def input_writeln(self, *args, **kwargs): args.append("\n") self.input_write(*args, **kwargs) - def output_gen(self, shell_cmd): - """output_gen(self, shell_cmd) -> None + def output_gen(self, shell_cmd, time_limit=None): + """output_gen(self, shell_cmd, time_limit) -> None Run the command shell_cmd(usually the std programme) and send it the input file as stdin. Write its output to the output file. - str shell_cmd -> the command to run, usually the std programme + str shell_cmd -> the command to run, usually the std program. + int | None time_limit -> time limit (seconds) of the command to run. """ self.flush_buffer() origin_pos = self.input_file.tell() self.input_file.seek(0) - subprocess.check_call( - shell_cmd, - shell=True, - stdin=self.input_file, - stdout=self.output_file, - universal_newlines=True, - ) + if time_limit is not None: + subprocess.check_call( + shell_cmd, + shell=True, + timeout=time_limit, + stdin=self.input_file, + stdout=self.output_file, + universal_newlines=True, + ) + else: + subprocess.check_call( + shell_cmd, + shell=True, + stdin=self.input_file, + stdout=self.output_file, + universal_newlines=True, + ) self.input_file.seek(origin_pos) log.debug(self.output_filename, " done") diff --git a/cyaron/log.py b/cyaron/log.py index 2dbb922..33b3b0c 100644 --- a/cyaron/log.py +++ b/cyaron/log.py @@ -2,30 +2,24 @@ from functools import partial import sys from threading import Lock - try: import colorful except ImportError: - class colorful: def __getattr__(self, attr): return lambda st: st - colorful = colorful() from .utils import make_unicode __print = print - - def _print(*args, **kwargs): flush = False - if "flush" in kwargs: - flush = kwargs["flush"] - del kwargs["flush"] + if 'flush' in kwargs: + flush = kwargs['flush'] + del kwargs['flush'] __print(*args, **kwargs) if flush: - kwargs.get("file", sys.stdout).flush() - + kwargs.get('file', sys.stdout).flush() def _join_dict(a, b): """join two dict""" @@ -34,11 +28,8 @@ def _join_dict(a, b): c[k] = v return c - _log_funcs = {} _log_lock = Lock() - - def log(funcname, *args, **kwargs): """log with log function specified by ``funcname``""" _log_lock.acquire() @@ -46,7 +37,6 @@ def log(funcname, *args, **kwargs): _log_lock.release() return rv - """5 log levels 1. debug: debug info 2. info: common info @@ -55,12 +45,11 @@ def log(funcname, *args, **kwargs): 5. error: errors """ -debug = partial(log, "debug") -info = partial(log, "info") -print = partial(log, "print") -warn = partial(log, "warn") -error = partial(log, "error") - +debug = partial(log, 'debug') +info = partial(log, 'info') +print = partial(log, 'print') +warn = partial(log, 'warn') +error = partial(log, 'error') def register_logfunc(funcname, func): """register logfunc @@ -75,21 +64,10 @@ def register_logfunc(funcname, func): except KeyError: pass - -_nb_print = lambda *args, **kwargs: _print(*args, **_join_dict(kwargs, {"flush": True})) -_nb_print_e = lambda *args, **kwargs: _print( - *args, **_join_dict(kwargs, {"file": sys.stderr, "flush": True}) -) -_cl_print = lambda color, *args, **kwargs: ( - _nb_print(*[color(make_unicode(item)) for item in args], **kwargs) - if sys.stdout.isatty() - else _nb_print(*args, **kwargs) -) -_cl_print_e = lambda color, *args, **kwargs: ( - _nb_print_e(*[color(make_unicode(item)) for item in args], **kwargs) - if sys.stderr.isatty() - else _nb_print_e(*args, **kwargs) -) +_nb_print = lambda *args, **kwargs: _print(*args, **_join_dict(kwargs, {'flush': True})) +_nb_print_e = lambda *args, **kwargs: _print(*args, **_join_dict(kwargs, {'file': sys.stderr, 'flush': True})) +_cl_print = lambda color, *args, **kwargs: _nb_print(*[color(make_unicode(item)) for item in args], **kwargs) if sys.stdout.isatty() else _nb_print(*args, **kwargs) +_cl_print_e = lambda color, *args, **kwargs: _nb_print_e(*[color(make_unicode(item)) for item in args], **kwargs) if sys.stderr.isatty() else _nb_print_e(*args, **kwargs) _default_debug = partial(_cl_print, colorful.cyan) _default_info = partial(_cl_print, colorful.blue) @@ -97,32 +75,28 @@ def register_logfunc(funcname, func): _default_warn = partial(_cl_print_e, colorful.yellow) _default_error = partial(_cl_print_e, colorful.red) - def set_quiet(): """set log mode to "quiet" """ - register_logfunc("debug", None) - register_logfunc("info", None) - register_logfunc("print", _default_print) - register_logfunc("warn", None) - register_logfunc("error", _default_error) - + register_logfunc('debug', None) + register_logfunc('info', None) + register_logfunc('print', _default_print) + register_logfunc('warn', None) + register_logfunc('error', _default_error) def set_normal(): """set log mode to "normal" """ - register_logfunc("debug", None) - register_logfunc("info", _default_info) - register_logfunc("print", _default_print) - register_logfunc("warn", _default_warn) - register_logfunc("error", _default_error) - + register_logfunc('debug', None) + register_logfunc('info', _default_info) + register_logfunc('print', _default_print) + register_logfunc('warn', _default_warn) + register_logfunc('error', _default_error) def set_verbose(): """set log mode to "verbose" """ - register_logfunc("debug", _default_debug) - register_logfunc("info", _default_info) - register_logfunc("print", _default_print) - register_logfunc("warn", _default_warn) - register_logfunc("error", _default_error) - + register_logfunc('debug', _default_debug) + register_logfunc('info', _default_info) + register_logfunc('print', _default_print) + register_logfunc('warn', _default_warn) + register_logfunc('error', _default_error) set_normal() diff --git a/cyaron/math.py b/cyaron/math.py index a23d332..eb7379d 100644 --- a/cyaron/math.py +++ b/cyaron/math.py @@ -1,7 +1,7 @@ -# coding=utf8 -""" +#coding=utf8 +''' forked from https://blog.dreamshire.com/common-functions-routines-project-euler/ -""" +''' from __future__ import absolute_import from math import sqrt, ceil, gcd from functools import reduce @@ -10,10 +10,8 @@ fact = (1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880) - - -def help(): # Give help informations - help_txt = """ +def help(): #Give help informations + help_txt=""" Welcome to CYaRon/math.py help! Functions are: | factorial(n) - The factorial of n @@ -42,73 +40,49 @@ def help(): # Give help informations """ print(help_txt) +def factorial(n): return reduce(lambda x,y:x*y,range(1,n+1),1) -def factorial(n): - return reduce(lambda x, y: x * y, range(1, n + 1), 1) - - -def is_perm(a, b): - return sorted(str(a)) == sorted(str(b)) - +def is_perm(a,b): return sorted(str(a))==sorted(str(b)) -def is_palindromic(n): - n = str(n) - return n == n[::-1] +def is_palindromic(n): n=str(n); return n==n[::-1] +def is_pandigital(n, s=9): n=str(n); return len(n)==s and not '1234567890'[:s].strip(n) -def is_pandigital(n, s=9): - n = str(n) - return len(n) == s and not "1234567890"[:s].strip(n) - - -# --- Calculate the sum of proper divisors for n-------------------------------------------------- +#--- Calculate the sum of proper divisors for n-------------------------------------------------- def d(n): s = 1 t = sqrt(n) - for i in range(2, int(t) + 1): - if n % i == 0: - s += i + n / i - if t == int(t): - s -= t # correct s if t is a perfect square + for i in range(2, int(t)+1): + if n % i == 0: s += i + n/i + if t == int(t): s -= t #correct s if t is a perfect square return s - -# --- Create a list of all palindromic numbers with k digits-------------------------------------- +#--- Create a list of all palindromic numbers with k digits-------------------------------------- def pal_list(k): if k == 1: return [1, 2, 3, 4, 5, 6, 7, 8, 9] - return [ - sum( - [ - n * (10**i) - for i, n in enumerate( - ([x] + list(ys) + [z] + list(ys)[::-1] + [x]) - if k % 2 - else ([x] + list(ys) + list(ys)[::-1] + [x]) - ) - ] - ) - for x in range(1, 10) - for ys in itertools.product(range(10), repeat=int(k / 2) - 1) - for z in (range(10) if k % 2 else (None,)) - ] - - -# --- sum of factorial's digits------------------------------------------------------------------- + return [sum([n*(10**i) for i,n in enumerate(([x]+list(ys)+[z]+list(ys)[::-1]+[x]) if k%2 + else ([x]+list(ys)+list(ys)[::-1]+[x]))]) + for x in range(1,10) + for ys in itertools.product(range(10), repeat=int(k/2)-1) + for z in (range(10) if k%2 else (None,))] + + +#--- sum of factorial's digits------------------------------------------------------------------- def sof_digits(n): - if n == 0: - return 1 + if n==0: return 1 s = 0 while n > 0: s, n = s + fact[n % 10], n // 10 return s -# --- find the nth Fibonacci number--------------------------------------------------------------- + +#--- find the nth Fibonacci number--------------------------------------------------------------- def fibonacci(n): """ Find the nth number in the Fibonacci series. Example: - + >>>fibonacci(100) 354224848179261915075 @@ -120,7 +94,6 @@ def fibonacci(n): raise ValueError("Negative arguments not implemented") return _fib(n)[0] - # Returns a tuple (F(n), F(n+1)) def _fib(n): if n == 0: @@ -135,40 +108,38 @@ def _fib(n): return (d, c + d) -# --- sum of squares of digits------------------------------------------------------------------- +#--- sum of squares of digits------------------------------------------------------------------- def sos_digits(n): s = 0 while n > 0: - s, n = s + (n % 10) ** 2, n // 10 + s, n = s + (n % 10)**2, n // 10 return s - -# --- sum of the digits to a power e------------------------------------------------------------- +#--- sum of the digits to a power e------------------------------------------------------------- def pow_digits(n, e): s = 0 while n > 0: - s, n = s + (n % 10) ** e, n // 10 + s, n = s + (n % 10)**e, n // 10 return s -# --- check n for prime-------------------------------------------------------------------------- + +#--- check n for prime-------------------------------------------------------------------------- def is_prime(n): - if n <= 1: - return False - if n <= 3: - return True - if n % 2 == 0 or n % 3 == 0: - return False + if n <= 1: return False + if n <= 3: return True + if n%2==0 or n%3 == 0: return False r = int(sqrt(n)) f = 5 while f <= r: - if n % f == 0 or n % (f + 2) == 0: - return False - f += 6 + if n%f == 0 or n%(f+2) == 0: return False + f+= 6 return True -# --- Miller-Rabin primality test---------------------------------------------------------------- + + +#--- Miller-Rabin primality test---------------------------------------------------------------- def miller_rabin(n): """ Check n for primalty: Example: @@ -192,33 +163,33 @@ def miller_rabin(n): return False return True - def miller_rabin_pass(a, s, d, n): a_to_power = pow(a, d, n) if a_to_power == 1: return True - for i in range(s - 1): + for i in range(s-1): if a_to_power == n - 1: return True a_to_power = (a_to_power * a_to_power) % n return a_to_power == n - 1 -# --- factor a number into primes and frequency---------------------------------------------------- + +#--- factor a number into primes and frequency---------------------------------------------------- def factor(n): """ - find the prime factors of n along with their frequencies. Example: + find the prime factors of n along with their frequencies. Example: - >>> factor(786456) - [(2,3), (3,3), (11,1), (331,1)] + >>> factor(786456) + [(2,3), (3,3), (11,1), (331,1)] - Source: Project Euler forums for problem #3 + Source: Project Euler forums for problem #3 """ f, factors, prime_gaps = 1, [], [2, 4, 2, 4, 6, 2, 6, 4] if n < 1: return [] while True: - for gap in [1, 1, 2, 2, 4] if f < 11 else prime_gaps: + for gap in ([1, 1, 2, 2, 4] if f < 11 else prime_gaps): f += gap if f * f > n: # If f > sqrt(n) if n == 1: @@ -234,7 +205,7 @@ def factor(n): factors.append((f, e)) -# --- generate permutations----------------------------------------------------------------------- +#--- generate permutations----------------------------------------------------------------------- def perm(n, s): """ requires function factorial() @@ -243,41 +214,43 @@ def perm(n, s): >>>perm(30, 'abcde') bcade """ - if len(s) == 1: - return s - q, r = divmod(n, factorial(len(s) - 1)) - return s[q] + perm(r, s[:q] + s[q + 1 :]) + if len(s)==1: return s + q, r = divmod(n, factorial(len(s)-1)) + return s[q] + perm(r, s[:q] + s[q+1:]) + -# --- binomial coefficients----------------------------------------------------------------------- + +#--- binomial coefficients----------------------------------------------------------------------- def binomial(n, k): """ Calculate C(n,k), the number of ways can k be chosen from n. Example: - + >>>binomial(30,12) 86493225 """ nt = 1 - for t in range(min(k, n - k)): - nt = nt * (n - t) // (t + 1) + for t in range(min(k, n-k)): + nt = nt * (n-t) // (t+1) return nt -# --- catalan number------------------------------------------------------------------------------ +#--- catalan number------------------------------------------------------------------------------ def catalan_number(n): """ Calculate the nth Catalan number. Example: - + >>>catalan_number(10) 16796 """ nm = dm = 1 - for k in range(2, n + 1): - nm, dm = (nm * (n + k), dm * k) + for k in range(2, n+1): + nm, dm = (nm*(n+k), dm*k) return nm / dm -# --- generate prime numbers---------------------------------------------------------------------- + +#--- generate prime numbers---------------------------------------------------------------------- def prime_sieve(n): """ Return a list of prime numbers from 2 to a prime < n. Very fast (n<10,000,000) in 0.4 sec. @@ -289,15 +262,16 @@ def prime_sieve(n): Algorithm & Python source: Robert William Hanks http://stackoverflow.com/questions/17773352/python-sieve-prime-numbers """ - sieve = [True] * (n // 2) - for i in range(3, int(n**0.5) + 1, 2): - if sieve[i // 2]: - sieve[i * i // 2 :: i] = [False] * ((n - i * i - 1) // (2 * i) + 1) - return [2] + [2 * i + 1 for i in range(1, n // 2) if sieve[i]] + sieve = [True] * (n//2) + for i in range(3,int(n**0.5)+1,2): + if sieve[i//2]: + sieve[i*i//2::i] = [False] * ((n-i*i-1)//(2*i)+1) + return [2] + [2*i+1 for i in range(1,n//2) if sieve[i]] -# --- bezout coefficients-------------------------------------------------------------------------- -def exgcd(a, b): + +#--- bezout coefficients-------------------------------------------------------------------------- +def exgcd(a,b): """ Bézout coefficients (u,v) of (a,b) as: @@ -313,131 +287,85 @@ def exgcd(a, b): Algorithm source: Pierre L. Douillet http://www.douillet.info/~douillet/working_papers/bezout/node2.html """ - u, v, s, t = 1, 0, 0, 1 - while b != 0: - q, r = divmod(a, b) + u, v, s, t = 1, 0, 0, 1 + while b !=0: + q, r = divmod(a,b) a, b = b, r - u, s = s, u - q * s - v, t = t, v - q * t + u, s = s, u - q*s + v, t = t, v - q*t return (u, v, a) - -def mod_inverse(a, b): - x, y, z = exgcd(a, b) - return x - + + +def mod_inverse(a,b): + x,y,z = exgcd(a,b) + return x; def phi(x): - if x == 1: - return 1 - factors = factor(x) - ans = x + if x==1: + return 1; + factors = factor(x); + ans = x; for prime in factors: - ans = int(ans / prime[0] * (prime[0] - 1)) + ans=int(ans / prime[0]*(prime[0]-1)) return ans - def miu(x): - if x == 1: - return 1 + if x==1: + return 1; factors = factor(x) for prime in factors: - if prime[1] > 1: - return 0 - return 1 - (len(factors) and 1) * 2 - - -# --- number base conversion ------------------------------------------------------------------- -# source: http://interactivepython.org/runestone/static/pythonds/Recursion/pythondsConvertinganIntegertoaStringinAnyBase.html -def dec2base(n, base): - convertString = "0123456789ABCDEF" - if n < base: - return convertString[n] - else: - return dec2base(n // base, base) + convertString[n % base] - - -# --- number to words ---------------------------------------------------------------------------- -# this function copied from stackoverflow user: Developer, Oct 5 '13 at 3:45 -def n2words(num, join=True): - """words = {} convert an integer number into words""" - units = ["", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"] - teens = [ - "", - "Eleven", - "Twelve", - "Thirteen", - "Fourteen", - "Fifteen", - "Sixteen", - "Seventeen", - "Eighteen", - "Nineteen", - ] - tens = [ - "", - "Ten", - "Twenty", - "Thirty", - "Forty", - "Fifty", - "Sixty", - "Seventy", - "Eighty", - "Ninety", - ] - thousands = [ - "", - "Thousand", - "Million", - "Billion", - "Trillion", - "Quadrillion", - "Quintillion", - "Sextillion", - "Septillion", - "Octillion", - "Nonillion", - "Decillion", - "Undecillion", - "Duodecillion", - "Tredecillion", - "Quattuordecillion", - "Sexdecillion", - "Septendecillion", - "Octodecillion", - "Novemdecillion", - "Vigintillion", - ] + if prime[1]>1: + return 0; + return 1-(len(factors) and 1)*2 + + +#--- number base conversion ------------------------------------------------------------------- +#source: http://interactivepython.org/runestone/static/pythonds/Recursion/pythondsConvertinganIntegertoaStringinAnyBase.html +def dec2base(n,base): + convertString = "0123456789ABCDEF" + if n < base: + return convertString[n] + else: + return dec2base(n//base,base) + convertString[n%base] + +#--- number to words ---------------------------------------------------------------------------- +#this function copied from stackoverflow user: Developer, Oct 5 '13 at 3:45 +def n2words(num,join=True): + '''words = {} convert an integer number into words''' + units = ['','One','Two','Three','Four','Five','Six','Seven','Eight','Nine'] + teens = ['','Eleven','Twelve','Thirteen','Fourteen','Fifteen','Sixteen', \ + 'Seventeen','Eighteen','Nineteen'] + tens = ['','Ten','Twenty','Thirty','Forty','Fifty','Sixty','Seventy', \ + 'Eighty','Ninety'] + thousands = ['','Thousand','Million','Billion','Trillion','Quadrillion', \ + 'Quintillion','Sextillion','Septillion','Octillion', \ + 'Nonillion','Decillion','Undecillion','Duodecillion', \ + 'Tredecillion','Quattuordecillion','Sexdecillion', \ + 'Septendecillion','Octodecillion','Novemdecillion', \ + 'Vigintillion'] words = [] - if num == 0: - words.append("zero") + if num==0: words.append('zero') else: - numStr = "%d" % num + numStr = '%d'%num numStrLen = len(numStr) - groups = int((numStrLen + 2) / 3) - numStr = numStr.zfill(groups * 3) - for i in range(0, groups * 3, 3): - h, t, u = int(numStr[i]), int(numStr[i + 1]), int(numStr[i + 2]) - g = groups - (int(i / 3) + 1) - if h >= 1: + groups = int((numStrLen+2)/3) + numStr = numStr.zfill(groups*3) + for i in range(0,groups*3,3): + h,t,u = int(numStr[i]),int(numStr[i+1]),int(numStr[i+2]) + g = groups-(int(i/3)+1) + if h>=1: words.append(units[h]) - words.append("Hundred") - if t > 1: + words.append('Hundred') + if t>1: words.append(tens[t]) - if u >= 1: - words.append(units[u]) - elif t == 1: - if u >= 1: - words.append(teens[u]) - else: - words.append(tens[t]) + if u>=1: words.append(units[u]) + elif t==1: + if u>=1: words.append(teens[u]) + else: words.append(tens[t]) else: - if u >= 1: - words.append(units[u]) - if (g >= 1) and ((h + t + u) > 0): - words.append(thousands[g] + "") - if join: - return " ".join(words) + if u>=1: words.append(units[u]) + if (g>=1) and ((h+t+u)>0): words.append(thousands[g]+'') + if join: return ' '.join(words) return words diff --git a/cyaron/merger.py b/cyaron/merger.py index 41847c3..e7069b6 100644 --- a/cyaron/merger.py +++ b/cyaron/merger.py @@ -1,57 +1,55 @@ from .graph import * - class Merger: def __init__(self, *graphs, **kwargs): """__init__(self, *graphs, **kwargs) -> None - put several graphs into one - list graphs -> the graphs that will be merged - list kwargs: - None + put several graphs into one + list graphs -> the graphs that will be merged + list kwargs: + None """ self.graphs = graphs self.G = Graph(sum([len(i.edges) - 1 for i in graphs]), graphs[0].directed) - - counter = 0 + + counter = 0 for graph in self.graphs: graph.offset = counter for edge in graph.iterate_edges(): - self.G.add_edge( - edge.start + counter, edge.end + counter, weight=edge.weight - ) + self.G.add_edge(edge.start + counter, + edge.end + counter, + weight=edge.weight) counter += len(graph.edges) - 1 def __add_edge(self, u, v, **kwargs): """__add_edge(self, u, v, **kwargs) - tuple u -> (graph_index, vertex) indicating the start point - tuple v -> (graph_index, vertex) indicating the end point - **kwargs: - int weight -> edge weight + tuple u -> (graph_index, vertex) indicating the start point + tuple v -> (graph_index, vertex) indicating the end point + **kwargs: + int weight -> edge weight """ - self.G.add_edge( - self.graphs[u[0]].offset + u[1], - self.graphs[v[0]].offset + v[1], - weight=kwargs.get("weight", 1), - ) - + self.G.add_edge(self.graphs[ u[0] ].offset + u[1], + self.graphs[ v[0] ].offset + v[1], + weight=kwargs.get("weight", 1)) + def add_edge(self, u, v, **kwargs): - """add_edge(self, u, v, **kwargs) -> None""" + """add_edge(self, u, v, **kwargs) -> None + """ self.__add_edge(u, v, **kwargs) def to_str(self, **kwargs): return self.G.to_str(**kwargs) - + def __str__(self): return self.to_str() @staticmethod def component(point_count, edge_count, **kwargs): """component(point_count, edge_count, **kwargs) - generate a graph with certain components - int point_count -> the number of vertices of each component - int edge_count -> the number of edges of each component - **kwargs: - int component_count -> indicating how many components there are + generate a graph with certain components + int point_count -> the number of vertices of each component + int edge_count -> the number of edges of each component + **kwargs: + int component_count -> indicating how many components there are """ component_count = kwargs.get("component_count", (2, 2)) if not list_like(component_count): diff --git a/cyaron/output_capture.py b/cyaron/output_capture.py index 5680526..4bcac58 100644 --- a/cyaron/output_capture.py +++ b/cyaron/output_capture.py @@ -1,12 +1,10 @@ from contextlib import contextmanager import sys - try: from StringIO import StringIO except ImportError: from io import StringIO - @contextmanager def captured_output(): new_out, new_err = StringIO(), StringIO() @@ -15,4 +13,4 @@ def captured_output(): sys.stdout, sys.stderr = new_out, new_err yield sys.stdout, sys.stderr finally: - sys.stdout, sys.stderr = old_out, old_err + sys.stdout, sys.stderr = old_out, old_err \ No newline at end of file diff --git a/cyaron/polygon.py b/cyaron/polygon.py index d73fdec..d0dfca8 100644 --- a/cyaron/polygon.py +++ b/cyaron/polygon.py @@ -6,7 +6,7 @@ class Polygon: - def __init__(self, points=[]): + def __init__(self,points=[]): if not list_like(points): raise Exception("polygon must be constructed by a list of points") self.points = points @@ -15,16 +15,15 @@ def __str__(self): buf = [] for point in self.points: buf.append(str(point[0]) + " " + str(point[1])) - return "\n".join(buf) + return '\n'.join(buf) def perimeter(self): ans = 0 for i in range(0, len(self.points)): a = self.points[i] b = self.points[(i + 1) % len(self.points)] - ans = ans + math.sqrt( - (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) - ) + ans = ans + math.sqrt((a[0] - b[0]) * (a[0] - b[0]) + + (a[1] - b[1]) * (a[1] - b[1])) return ans def area(self): @@ -38,8 +37,8 @@ def area(self): ans = ans / 2.0 return ans - # generate a convex hull with n points - # it's possible to have even edges + #generate a convex hull with n points + #it's possible to have even edges @staticmethod def convex_hull(n, **kwargs): # fx, fy are functions which map [0,1] to int or float @@ -76,7 +75,8 @@ def convex_hull(n, **kwargs): a = st[len(st) - 1] b = points[i] o = st[len(st) - 2] - tmp = (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) + tmp = (a[0] - o[0]) * (b[1] - o[1]) - \ + (a[1] - o[1]) * (b[0] - o[0]) if tmp > 0 or (tmp == 0 and not strict): break st.pop() @@ -87,7 +87,8 @@ def convex_hull(n, **kwargs): a = st[len(st) - 1] b = points[i] o = st[len(st) - 2] - tmp = (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) + tmp = (a[0] - o[0]) * (b[1] - o[1]) - \ + (a[1] - o[1]) * (b[0] - o[0]) if tmp > 0 or (tmp == 0 and not strict): break st.pop() @@ -109,34 +110,21 @@ def __conquer(points): if len(points) <= 2: return points if len(points) == 3: - (points[1], points[2]) = (points[2], points[1]) + (points[1],points[2])=(points[2],points[1]) return points divide_id = random.randint(2, len(points) - 1) divide_point1 = points[divide_id] divide_k = random.uniform(0.01, 0.99) - divide_point2 = [ - divide_k * (points[1][0] - points[0][0]) + points[0][0], - divide_k * (points[1][1] - points[0][1]) + points[0][1], - ] + divide_point2 = [divide_k * (points[1][0] - points[0][0]) + points[0][0], + divide_k * (points[1][1] - points[0][1]) + points[0][1]] # path: points[0]->points[divide]->points[1] # dividing line in the form Ax+By+C=0 - divide_line = [ - divide_point2[1] - divide_point1[1], - divide_point1[0] - divide_point2[0], - -divide_point1[0] * divide_point2[1] + divide_point1[1] * divide_point2[0], - ] - p0 = ( - divide_line[0] * points[0][0] - + divide_line[1] * points[0][1] - + divide_line[2] - >= 0 - ) - p1 = ( - divide_line[0] * points[1][0] - + divide_line[1] * points[1][1] - + divide_line[2] - >= 0 - ) + divide_line = [divide_point2[1] - divide_point1[1], + divide_point1[0] - divide_point2[0], + -divide_point1[0] * divide_point2[1] + + divide_point1[1] * divide_point2[0]] + p0 = (divide_line[0] * points[0][0] + divide_line[1] * points[0][1] + divide_line[2] >= 0) + p1 = (divide_line[0] * points[1][0] + divide_line[1] * points[1][1] + divide_line[2] >= 0) if p0 == p1: # the divide point isn't good enough... return Polygon.__conquer(points) s = [[], []] @@ -147,12 +135,7 @@ def __conquer(points): for i in range(2, len(points)): if i == divide_id: continue - pt = ( - divide_line[0] * points[i][0] - + divide_line[1] * points[i][1] - + divide_line[2] - >= 0 - ) + pt = (divide_line[0] * points[i][0] + divide_line[1] * points[i][1] + divide_line[2] >= 0) s[pt].append(points[i]) pa = Polygon.__conquer(s[p0]) pb = Polygon.__conquer(s[not p0]) @@ -169,23 +152,17 @@ def simple_polygon(points): if len(points) <= 3: return Polygon(points) # divide by points[0], points[1] - divide_line = [ - points[1][1] - points[0][1], - points[0][0] - points[1][0], - -points[0][0] * points[1][1] + points[0][1] * points[1][0], - ] + divide_line = [points[1][1] - points[0][1], + points[0][0] - points[1][0], + -points[0][0] * points[1][1] + + points[0][1] * points[1][0]] s = [[], []] s[0].append(points[0]) s[0].append(points[1]) s[1].append(points[1]) s[1].append(points[0]) for i in range(2, len(points)): - pt = ( - divide_line[0] * points[i][0] - + divide_line[1] * points[i][1] - + divide_line[2] - >= 0 - ) + pt = (divide_line[0] * points[i][0] + divide_line[1] * points[i][1] + divide_line[2] >= 0) s[pt].append(points[i]) pa = Polygon.__conquer(s[0]) pb = Polygon.__conquer(s[1]) diff --git a/cyaron/sequence.py b/cyaron/sequence.py index c584a33..625456e 100644 --- a/cyaron/sequence.py +++ b/cyaron/sequence.py @@ -1,13 +1,13 @@ from .utils import * - class Sequence: - """Class Sequence: the tool class for sequences.""" + """Class Sequence: the tool class for sequences. + """ def __init__(self, formula, initial_values=()): """__init__(self, formula, initial_values=() -> None - Create a sequence object. - int formula(int, function) -> the formula function ... + Create a sequence object. + int formula(int, function) -> the formula function ... """ if not callable(formula): raise Exception("formula must be a function") @@ -30,4 +30,4 @@ def get(self, left_range, right_range=None): if right_range is None: return self.__get_one(left_range) - return [self.__get_one(i) for i in range(left_range, right_range + 1)] + return [self.__get_one(i) for i in range(left_range, right_range+1)] diff --git a/cyaron/string.py b/cyaron/string.py index 2a5694e..6af1e5c 100644 --- a/cyaron/string.py +++ b/cyaron/string.py @@ -53,9 +53,7 @@ def random_sentence(word_count_range, **kwargs): def random_paragraph(sentence_count_range, **kwargs): sentence_count = sentence_count_range if list_like(sentence_count_range): - sentence_count = random.randint( - sentence_count_range[0], sentence_count_range[1] - ) + sentence_count = random.randint(sentence_count_range[0], sentence_count_range[1]) word_count_range = kwargs.get("word_count_range", (6, 10)) @@ -97,18 +95,15 @@ def random_paragraph(sentence_count_range, **kwargs): sentences.append(string) - paragraph = reduce( - lambda x, y: x + random.choice(sentence_joiners) + y, sentences - ) + paragraph = reduce(lambda x, y: x + random.choice(sentence_joiners) + y, sentences) return paragraph @staticmethod def random_regular(*args, **kwargs): pattern = args limit_len = int(kwargs.get("limit", "10")) - if limit_len <= 1: - limit_len = 10 - if list_like(args): + if (limit_len <= 1): limit_len = 10 + if (list_like(args)): pattern = random.choice(args) _x = xeger.Xeger(limit=limit_len) return _x.xeger(pattern) diff --git a/cyaron/tests/compare_test.py b/cyaron/tests/compare_test.py index 01d4354..f820a9d 100644 --- a/cyaron/tests/compare_test.py +++ b/cyaron/tests/compare_test.py @@ -11,7 +11,6 @@ log.set_verbose() - class TestCompare(unittest.TestCase): def setUp(self): @@ -52,19 +51,16 @@ def test_noipstyle_incorrect(self): with captured_output() as (out, err): Compare.output("test_another_incorrect.out", std=io) except CompareMismatch as e: - self.assertEqual(e.name, "test_another_incorrect.out") + self.assertEqual(e.name, 'test_another_incorrect.out') e = e.mismatch - self.assertEqual(e.content, "test123\r\ntest124 ") - self.assertEqual(e.std, "test123 \ntest123\n\n") - self.assertEqual(str(e), "On line 2 column 7, read 4, expected 3.") + self.assertEqual(e.content, 'test123\r\ntest124 ') + self.assertEqual(e.std, 'test123 \ntest123\n\n') + self.assertEqual(str(e), 'On line 2 column 7, read 4, expected 3.') else: self.assertTrue(False) result = out.getvalue().strip() - self.assertEqual( - result, - "test_another_incorrect.out: !!!INCORRECT!!! On line 2 column 7, read 4, expected 3.", - ) + self.assertEqual(result, "test_another_incorrect.out: !!!INCORRECT!!! On line 2 column 7, read 4, expected 3.") def test_fulltext_program(self): with open("correct.py", "w") as f: @@ -81,31 +77,19 @@ def test_fulltext_program(self): try: with captured_output() as (out, err): - Compare.program( - "python correct.py", - "python incorrect.py", - std=io, - input=io, - grader="FullText", - ) + Compare.program("python correct.py", "python incorrect.py", std=io, input=io, grader="FullText") except CompareMismatch as e: - self.assertEqual(e.name, "python incorrect.py") + self.assertEqual(e.name, 'python incorrect.py') e = e.mismatch - self.assertEqual(e.content, "2\n") - self.assertEqual(e.std, "1\n") - self.assertEqual( - e.content_hash, - "53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3", - ) - self.assertEqual( - e.std_hash, - "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865", - ) + self.assertEqual(e.content, '2\n') + self.assertEqual(e.std, '1\n') + self.assertEqual(e.content_hash, '53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3') + self.assertEqual(e.std_hash, '4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865') else: self.assertTrue(False) result = out.getvalue().strip() - correct_out = "python correct.py: Correct \npython incorrect.py: !!!INCORRECT!!! Hash mismatch: read 53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3, expected 4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" + correct_out = 'python correct.py: Correct \npython incorrect.py: !!!INCORRECT!!! Hash mismatch: read 53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3, expected 4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865' self.assertEqual(result, correct_out) def test_file_input(self): @@ -122,38 +106,28 @@ def test_file_input(self): io.input_writeln("233") with captured_output() as (out, err): - Compare.program( - "python correct.py", - std_program="python std.py", - input=io, - grader="NOIPStyle", - ) + Compare.program("python correct.py", std_program="python std.py", input=io, grader="NOIPStyle") result = out.getvalue().strip() - correct_out = "python correct.py: Correct" + correct_out = 'python correct.py: Correct' self.assertEqual(result, correct_out) def test_concurrent(self): - programs = ["test{}.py".format(i) for i in range(16)] + programs = ['test{}.py'.format(i) for i in range(16)] for fn in programs: - with open(fn, "w") as f: - f.write("print({})".format(16)) - with open("std.py", "w") as f: - f.write("print({})".format(16)) + with open(fn, 'w') as f: + f.write('print({})'.format(16)) + with open('std.py', 'w') as f: + f.write('print({})'.format(16)) with IO() as test: - Compare.program( - *[(sys.executable, program) for program in programs], - std_program=(sys.executable, "std.py"), - max_workers=None, - input=test - ) + Compare.program(*[(sys.executable, program) for program in programs], std_program=(sys.executable, 'std.py'), max_workers=None, input=test) ios = [IO() for i in range(16)] try: for f in ios: - f.output_write("16") + f.output_write('16') with IO() as std: - std.output_write("16") + std.output_write('16') Compare.output(*ios, std=std, max_workers=None) finally: for io in ios: @@ -163,11 +137,7 @@ def test_timeout(self): if sys.version_info >= (3, 3): with IO() as test: try: - Compare.program( - ((sys.executable, "-c", "__import__('time').sleep(10)"), 1), - std=test, - input=test, - ) + Compare.program(((sys.executable, '-c', '__import__(\'time\').sleep(10)'), 1), std=test, input=test) except subprocess.TimeoutExpired: pass else: diff --git a/cyaron/tests/graph_test.py b/cyaron/tests/graph_test.py index 57b3e07..976930a 100644 --- a/cyaron/tests/graph_test.py +++ b/cyaron/tests/graph_test.py @@ -24,14 +24,14 @@ def test_same(self, l, r): def tarjan(graph, n): def new_array(len, val=0): - return [val for _ in range(len + 1)] + return [val for _ in range(len+1)] instack = new_array(n, False) low = new_array(n) dfn = new_array(n, 0) stap = new_array(n) belong = new_array(n) - var = [0, 0, 0] # cnt, bc, stop + var = [0, 0, 0] # cnt, bc, stop # cnt = bc = stop = 0 def dfs(cur): @@ -49,7 +49,7 @@ def dfs(cur): low[cur] = min(low[cur], dfn[v.end]) if dfn[cur] == low[cur]: - v = cur + 1 # set v != cur + v = cur + 1 # set v != cur var[1] += 1 while v != cur: var[2] -= 1 @@ -58,8 +58,8 @@ def dfs(cur): belong[v] = var[1] for i in range(n): - if dfn[i + 1] == 0: - dfs(i + 1) + if dfn[i+1] == 0: + dfs(i+1) return belong @@ -69,20 +69,20 @@ class TestGraph(unittest.TestCase): def test_self_loop(self): graph_size = 20 for _ in range(20): - graph = Graph.graph(graph_size, int(graph_size * 2), self_loop=True) + graph = Graph.graph(graph_size, int(graph_size*2), self_loop=True) has_self_loop = max([e.start == e.end for e in graph.iterate_edges()]) if has_self_loop: break self.assertTrue(has_self_loop) for _ in range(10): - graph = Graph.graph(graph_size, int(graph_size * 2), self_loop=False) + graph = Graph.graph(graph_size, int(graph_size*2), self_loop=False) self.assertFalse(max([e.start == e.end for e in graph.iterate_edges()])) def test_repeated_edges(self): graph_size = 20 for _ in range(20): - graph = Graph.graph(graph_size, int(graph_size * 2), repeated_edges=True) + graph = Graph.graph(graph_size, int(graph_size*2), repeated_edges=True) edges = [(e.start, e.end) for e in graph.iterate_edges()] has_repeated_edges = len(edges) > len(set(edges)) if has_repeated_edges: @@ -90,7 +90,7 @@ def test_repeated_edges(self): self.assertTrue(has_repeated_edges) for _ in range(10): - graph = Graph.graph(graph_size, int(graph_size * 2), repeated_edges=False) + graph = Graph.graph(graph_size, int(graph_size*2), repeated_edges=False) edges = list(graph.iterate_edges()) self.assertEqual(len(edges), len(set(edges))) @@ -101,69 +101,53 @@ def test_tree_connected(self): tree = Graph.tree(graph_size) for edge in tree.iterate_edges(): ufs.merge(edge.start, edge.end) - for i in range(graph_size - 1): - self.assertTrue(ufs.test_same(i + 1, i + 2)) + for i in range(graph_size-1): + self.assertTrue(ufs.test_same(i+1, i+2)) + def test_DAG(self): graph_size = 20 - for _ in range(10): # test 10 times + for _ in range(10): # test 10 times ufs = UnionFindSet(graph_size) - graph = Graph.DAG( - graph_size, - int(graph_size * 1.6), - repeated_edges=False, - self_loop=False, - loop=True, - ) + graph = Graph.DAG(graph_size, int(graph_size*1.6), repeated_edges=False, self_loop=False, loop=True) - self.assertEqual(len(list(graph.iterate_edges())), int(graph_size * 1.6)) + self.assertEqual(len(list(graph.iterate_edges())), int(graph_size*1.6)) for edge in graph.iterate_edges(): ufs.merge(edge.start, edge.end) - for i in range(graph_size - 1): - self.assertTrue(ufs.test_same(i + 1, i + 2)) + for i in range(graph_size-1): + self.assertTrue(ufs.test_same(i+1, i+2)) def test_DAG_without_loop(self): graph_size = 20 - for _ in range(10): # test 10 times + for _ in range(10): # test 10 times ufs = UnionFindSet(graph_size) - graph = Graph.DAG( - graph_size, - int(graph_size * 1.6), - repeated_edges=False, - self_loop=False, - loop=False, - ) + graph = Graph.DAG(graph_size, int(graph_size*1.6), repeated_edges=False, self_loop=False, loop=False) - self.assertEqual(len(list(graph.iterate_edges())), int(graph_size * 1.6)) + self.assertEqual(len(list(graph.iterate_edges())), int(graph_size*1.6)) for edge in graph.iterate_edges(): ufs.merge(edge.start, edge.end) - for i in range(graph_size - 1): - self.assertTrue(ufs.test_same(i + 1, i + 2)) + for i in range(graph_size-1): + self.assertTrue(ufs.test_same(i+1, i+2)) belong = tarjan(graph, graph_size) self.assertEqual(max(belong), graph_size) def test_undirected_graph(self): graph_size = 20 - for _ in range(10): # test 10 times + for _ in range(10): # test 10 times ufs = UnionFindSet(graph_size) - graph = Graph.UDAG( - graph_size, int(graph_size * 1.6), repeated_edges=False, self_loop=False - ) + graph = Graph.UDAG(graph_size, int(graph_size*1.6), repeated_edges=False, self_loop=False) - self.assertEqual(len(list(graph.iterate_edges())), int(graph_size * 1.6)) + self.assertEqual(len(list(graph.iterate_edges())), int(graph_size*1.6)) for edge in graph.iterate_edges(): ufs.merge(edge.start, edge.end) - for i in range(graph_size - 1): - self.assertTrue(ufs.test_same(i + 1, i + 2)) + for i in range(graph_size-1): + self.assertTrue(ufs.test_same(i+1, i+2)) def test_DAG_boundary(self): - with self.assertRaises( - Exception, - msg="the number of edges of connected graph must more than the number of nodes - 1", - ): + with self.assertRaises(Exception, msg="the number of edges of connected graph must more than the number of nodes - 1"): Graph.DAG(8, 6) Graph.DAG(8, 7) diff --git a/cyaron/tests/io_test.py b/cyaron/tests/io_test.py index e8d88a2..b83dc03 100644 --- a/cyaron/tests/io_test.py +++ b/cyaron/tests/io_test.py @@ -2,6 +2,7 @@ import os import shutil import tempfile +import subprocess from cyaron import IO from cyaron.output_capture import captured_output @@ -69,8 +70,34 @@ def test_output_gen(self): output = f.read() self.assertEqual(output.strip("\n"), "233") - def test_output_gen_limits(self): - + def test_output_gen_time_limit_exceeded(self): + time_limit_exceeded = False + with captured_output() as (out, err): + with open("long_time.py", "w") as f: + f.write("import time\ntime.sleep(10)\nprint(1)") + + try: + with IO("test_gen.in", "test_gen.out") as test: + test.output_gen("python long_time.py", time_limit=1) + except subprocess.TimeoutExpired: + time_limit_exceeded = True + self.assertEqual(time_limit_exceeded, True) + + def test_output_gen_time_limit_not_exceeded(self): + time_limit_exceeded = False + with captured_output() as (out, err): + with open("short_time.py", "w") as f: + f.write("import time\ntime.sleep(0.2)\nprint(1)") + + try: + with IO("test_gen.in", "test_gen.out") as test: + test.output_gen("python short_time.py", time_limit=1) + except subprocess.TimeoutExpired: + time_limit_exceeded = True + with open("test_gen.out") as f: + output = f.read() + self.assertEqual(output.strip("\n"), "1") + self.assertEqual(time_limit_exceeded, False) def test_init_overload(self): with IO(file_prefix="data{", data_id=5) as test: diff --git a/cyaron/tests/polygon_test.py b/cyaron/tests/polygon_test.py index cac0ada..7548f3d 100644 --- a/cyaron/tests/polygon_test.py +++ b/cyaron/tests/polygon_test.py @@ -4,9 +4,7 @@ class TestPolygon(unittest.TestCase): def test_convex_hull(self): - hull = Polygon.convex_hull( - 300, fx=lambda x: int(x * 100000), fy=lambda x: int(x * 100000) - ) + hull = Polygon.convex_hull(300, fx=lambda x: int(x * 100000), fy=lambda x: int(x * 100000)) points = hull.points points = sorted(points) # unique @@ -21,7 +19,8 @@ def test_convex_hull(self): a = st[len(st) - 1] b = points[i] o = st[len(st) - 2] - if (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) >= 0: + if (a[0] - o[0]) * (b[1] - o[1]) - \ + (a[1] - o[1]) * (b[0] - o[0]) >= 0: break st.pop() st.append(points[i]) @@ -31,7 +30,8 @@ def test_convex_hull(self): a = st[len(st) - 1] b = points[i] o = st[len(st) - 2] - if (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) >= 0: + if (a[0] - o[0]) * (b[1] - o[1]) - \ + (a[1] - o[1]) * (b[0] - o[0]) >= 0: break st.pop() st.append(points[i]) @@ -55,10 +55,8 @@ def test_simple_polygon(self): c = points[j] d = points[(j + 1) % len(points)] prod = lambda x, y: x[0] * y[1] - x[1] * y[0] - t1 = prod( - [c[0] - a[0], c[1] - a[1]], [d[0] - a[0], d[1] - a[1]] - ) * prod([c[0] - b[0], c[1] - b[1]], [d[0] - b[0], d[1] - b[1]]) - t2 = prod( - [a[0] - c[0], a[1] - c[1]], [b[0] - c[0], b[1] - c[1]] - ) * prod([a[0] - d[0], a[1] - d[1]], [b[0] - d[0], b[1] - d[1]]) + t1 = prod([c[0] - a[0], c[1] - a[1]], [d[0] - a[0], d[1] - a[1]]) \ + * prod([c[0] - b[0], c[1] - b[1]], [d[0] - b[0], d[1] - b[1]]) + t2 = prod([a[0] - c[0], a[1] - c[1]], [b[0] - c[0], b[1] - c[1]]) \ + * prod([a[0] - d[0], a[1] - d[1]], [b[0] - d[0], b[1] - d[1]]) self.assertFalse(t1 <= 1e-9 and t2 <= 1e-9) diff --git a/cyaron/tests/sequence_test.py b/cyaron/tests/sequence_test.py index 035fcb3..272a1d5 100644 --- a/cyaron/tests/sequence_test.py +++ b/cyaron/tests/sequence_test.py @@ -20,3 +20,4 @@ def test_func_get_one(self): def test_func_get_many(self): seq = Sequence(lambda i, f: 3 * i + 2 * f(i - 1), [0]) self.assertEqual(seq.get(3, 5), [33, 78, 171]) + diff --git a/cyaron/tests/str_test.py b/cyaron/tests/str_test.py index 517a7ef..3f12fab 100644 --- a/cyaron/tests/str_test.py +++ b/cyaron/tests/str_test.py @@ -25,6 +25,6 @@ def test_random_paragraph(self): String.random_paragraph(10) def test_random_regular(self): - pattern = r"[0-9]+\w_.{0,9}" + pattern = r'[0-9]+\w_.{0,9}' sentence = String.random_regular(pattern, limit=5) - self.assertTrue(re.match(pattern, sentence).group() == sentence) + self.assertTrue(re.match(pattern, sentence).group() == sentence) \ No newline at end of file diff --git a/cyaron/utils.py b/cyaron/utils.py index 11a438e..4ef7358 100644 --- a/cyaron/utils.py +++ b/cyaron/utils.py @@ -1,14 +1,14 @@ def ati(array): """ati(array) -> list - Convert all the elements in the array and return them in a list. + Convert all the elements in the array and return them in a list. """ return [int(i) for i in array] def list_like(data): """list_like(data) -> bool - Judge whether the object data is like a list or a tuple. - object data -> the data to judge + Judge whether the object data is like a list or a tuple. + object data -> the data to judge """ return isinstance(data, tuple) or isinstance(data, list) @@ -24,7 +24,7 @@ def int_like(data): def strtolines(str): - lines = str.split("\n") + lines = str.split('\n') for i in range(len(lines)): lines[i] = lines[i].rstrip() @@ -39,7 +39,6 @@ def make_unicode(data): except NameError: return str(data) - def unpack_kwargs(funcname, kwargs, arg_pattern): rv = {} kwargs = kwargs.copy() @@ -59,15 +58,7 @@ def unpack_kwargs(funcname, kwargs, arg_pattern): except KeyError as e: error = True if error: - raise TypeError( - "{}() missing 1 required keyword-only argument: '{}'".format( - funcname, tp - ) - ) + raise TypeError('{}() missing 1 required keyword-only argument: \'{}\''.format(funcname, tp)) if kwargs: - raise TypeError( - "{}() got an unexpected keyword argument '{}'".format( - funcname, next(iter(kwargs.items()))[0] - ) - ) + raise TypeError('{}() got an unexpected keyword argument \'{}\''.format(funcname, next(iter(kwargs.items()))[0])) return rv diff --git a/cyaron/vector.py b/cyaron/vector.py index 3016883..2620c59 100644 --- a/cyaron/vector.py +++ b/cyaron/vector.py @@ -14,9 +14,7 @@ class VectorRandomMode(Enum): class Vector: @staticmethod - def random( - num: int = 5, position_range: list = None, mode: VectorRandomMode = 0, **kwargs - ): + def random(num: int = 5, position_range: list = None, mode: VectorRandomMode = 0, **kwargs): """ brief : generating n random vectors in limited space param : @@ -49,24 +47,16 @@ def random( else: offset.append(0) length.append(position_range[i]) - vector_space *= length[i] + 1 + vector_space *= (length[i] + 1) if mode == VectorRandomMode.unique and num > vector_space: - raise Exception( - "1st param is so large that CYaRon can not generate unique vectors" - ) + raise Exception("1st param is so large that CYaRon can not generate unique vectors") result = [] if mode == VectorRandomMode.repeatable: - result = [ - [random.randint(x, y) for x, y in zip(offset, length)] - for _ in range(num) - ] + result = [[random.randint(x, y) for x, y in zip(offset, length)] for _ in range(num)] elif mode == VectorRandomMode.float: - result = [ - [random.uniform(x, y) for x, y in zip(offset, length)] - for _ in range(num) - ] + result = [[random.uniform(x, y) for x, y in zip(offset, length)] for _ in range(num)] elif mode == VectorRandomMode.unique and vector_space > 5 * num: # O(NlogN) num_set = set() @@ -97,5 +87,5 @@ def get_vector(dimension: int, position_range: list, hashcode: int): tmp = [] for i in range(0, dimension): tmp.append(hashcode % (position_range[i] + 1)) - hashcode //= position_range[i] + 1 + hashcode //= (position_range[i] + 1) return tmp diff --git a/cyaron/visual.py b/cyaron/visual.py index 3e0f63d..7b5f122 100644 --- a/cyaron/visual.py +++ b/cyaron/visual.py @@ -1,26 +1,26 @@ -from .graph import * +from .graph import * from .merger import Merger import pygraphviz as pgv - def visualize(graph, output_path="a.png"): """visualize(graph, **kwargs) -> None - Graph/Merger graph -> the graph/Merger that will be visualized - string output_path-> the output path of the image + Graph/Merger graph -> the graph/Merger that will be visualized + string output_path-> the output path of the image """ - if isinstance(graph, Merger): - graph = Merger.G + if isinstance(graph, Merger): graph = Merger.G G = pgv.AGraph(directed=graph.directed) G.add_nodes_from([i for i in xrange(1, len(graph.edges))]) for edge in graph.iterate_edges(): G.add_edge(edge.start, edge.end, label=edge.weight) + + G.node_attr['shape'] = 'egg' + G.node_attr['width'] = '0.25' + G.node_attr['height'] = '0.25' + G.edge_attr['arrowhead'] = 'open' - G.node_attr["shape"] = "egg" - G.node_attr["width"] = "0.25" - G.node_attr["height"] = "0.25" - G.edge_attr["arrowhead"] = "open" - - G.layout(prog="dot") + G.layout(prog='dot') G.draw(output_path) + + From df0f379d33573015e1ff829e563a308940988cdf Mon Sep 17 00:00:00 2001 From: FredB-mine Date: Thu, 7 Mar 2024 20:46:48 +0800 Subject: [PATCH 3/4] Ignore macOS file system storage --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 94e2e44..7489c27 100644 --- a/.gitignore +++ b/.gitignore @@ -131,4 +131,6 @@ docs/_build/ target/ # Pycharm -venv \ No newline at end of file +venv + +*.DS_Store \ No newline at end of file From 1a9744c0673a0219016d6b0a33aa52e7ed8fc50e Mon Sep 17 00:00:00 2001 From: FredB-mine Date: Thu, 7 Mar 2024 20:54:40 +0800 Subject: [PATCH 4/4] Discard changes of compare.py --- cyaron/compare.py | 132 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 29 deletions(-) diff --git a/cyaron/compare.py b/cyaron/compare.py index ad613a0..f32c0d4 100644 --- a/cyaron/compare.py +++ b/cyaron/compare.py @@ -17,7 +17,7 @@ def __init__(self, name, mismatch): self.mismatch = mismatch def __str__(self): - return 'In program: \'{}\'. {}'.format(self.name,self.mismatch) + return "In program: '{}'. {}".format(self.name, self.mismatch) class Compare: @@ -37,7 +37,7 @@ def __process_file(file): file.output_file.seek(0) return file.output_filename, file.output_file.read() else: - with open(file, "r", newline='\n') as f: + with open(file, "r", newline="\n") as f: return file, f.read() @staticmethod @@ -50,26 +50,43 @@ def __normal_max_workers(workers): @classmethod def output(cls, *files, **kwargs): - kwargs = unpack_kwargs('output', kwargs, ('std', ('grader', DEFAULT_GRADER), ('max_workers', -1), - ('job_pool', None), ('stop_on_incorrect', None))) - std = kwargs['std'] - grader = kwargs['grader'] - max_workers = kwargs['max_workers'] - job_pool = kwargs['job_pool'] - if kwargs['stop_on_incorrect'] is not None: + kwargs = unpack_kwargs( + "output", + kwargs, + ( + "std", + ("grader", DEFAULT_GRADER), + ("max_workers", -1), + ("job_pool", None), + ("stop_on_incorrect", None), + ), + ) + std = kwargs["std"] + grader = kwargs["grader"] + max_workers = kwargs["max_workers"] + job_pool = kwargs["job_pool"] + if kwargs["stop_on_incorrect"] is not None: log.warn("parameter stop_on_incorrect is deprecated and has no effect.") if (max_workers is None or max_workers >= 0) and job_pool is None: max_workers = cls.__normal_max_workers(max_workers) try: from concurrent.futures import ThreadPoolExecutor + with ThreadPoolExecutor(max_workers=max_workers) as job_pool: - return cls.output(*files, std=std, grader=grader, max_workers=max_workers, job_pool=job_pool) + return cls.output( + *files, + std=std, + grader=grader, + max_workers=max_workers, + job_pool=job_pool + ) except ImportError: pass def get_std(): return cls.__process_file(std)[1] + if job_pool is not None: std = job_pool.submit(get_std).result() else: @@ -86,61 +103,118 @@ def do(file): @classmethod def program(cls, *programs, **kwargs): - kwargs = unpack_kwargs('program', kwargs, ('input', ('std', None), ('std_program', None), - ('grader', DEFAULT_GRADER), ('max_workers', -1), - ('job_pool', None), ('stop_on_incorrect', None))) - input = kwargs['input'] - std = kwargs['std'] - std_program = kwargs['std_program'] - grader = kwargs['grader'] - max_workers = kwargs['max_workers'] - job_pool = kwargs['job_pool'] - if kwargs['stop_on_incorrect'] is not None: + kwargs = unpack_kwargs( + "program", + kwargs, + ( + "input", + ("std", None), + ("std_program", None), + ("grader", DEFAULT_GRADER), + ("max_workers", -1), + ("job_pool", None), + ("stop_on_incorrect", None), + ), + ) + input = kwargs["input"] + std = kwargs["std"] + std_program = kwargs["std_program"] + grader = kwargs["grader"] + max_workers = kwargs["max_workers"] + job_pool = kwargs["job_pool"] + if kwargs["stop_on_incorrect"] is not None: log.warn("parameter stop_on_incorrect is deprecated and has no effect.") if (max_workers is None or max_workers >= 0) and job_pool is None: max_workers = cls.__normal_max_workers(max_workers) try: from concurrent.futures import ThreadPoolExecutor + with ThreadPoolExecutor(max_workers=max_workers) as job_pool: - return cls.program(*programs, input=input, std=std, std_program=std_program, grader=grader, max_workers=max_workers, job_pool=job_pool) + return cls.program( + *programs, + input=input, + std=std, + std_program=std_program, + grader=grader, + max_workers=max_workers, + job_pool=job_pool + ) except ImportError: pass if not isinstance(input, IO): - raise TypeError("expect {}, got {}".format(type(IO).__name__, type(input).__name__)) + raise TypeError( + "expect {}, got {}".format(type(IO).__name__, type(input).__name__) + ) input.flush_buffer() input.input_file.seek(0) if std_program is not None: + def get_std(): - with open(os.dup(input.input_file.fileno()), 'r', newline='\n') as input_file: - content = make_unicode(subprocess.check_output(std_program, shell=(not list_like(std_program)), stdin=input.input_file, universal_newlines=True)) + with open( + os.dup(input.input_file.fileno()), "r", newline="\n" + ) as input_file: + content = make_unicode( + subprocess.check_output( + std_program, + shell=(not list_like(std_program)), + stdin=input.input_file, + universal_newlines=True, + ) + ) input_file.seek(0) return content + if job_pool is not None: std = job_pool.submit(get_std).result() else: std = get_std() elif std is not None: + def get_std(): return cls.__process_file(std)[1] + if job_pool is not None: std = job_pool.submit(get_std).result() else: std = get_std() else: - raise TypeError('program() missing 1 required non-None keyword-only argument: \'std\' or \'std_program\'') + raise TypeError( + "program() missing 1 required non-None keyword-only argument: 'std' or 'std_program'" + ) def do(program_name): timeout = None - if list_like(program_name) and len(program_name) == 2 and int_like(program_name[-1]): + if ( + list_like(program_name) + and len(program_name) == 2 + and int_like(program_name[-1]) + ): program_name, timeout = program_name - with open(os.dup(input.input_file.fileno()), 'r', newline='\n') as input_file: + with open( + os.dup(input.input_file.fileno()), "r", newline="\n" + ) as input_file: if timeout is None: - content = make_unicode(subprocess.check_output(program_name, shell=(not list_like(program_name)), stdin=input_file, universal_newlines=True)) + content = make_unicode( + subprocess.check_output( + program_name, + shell=(not list_like(program_name)), + stdin=input_file, + universal_newlines=True, + ) + ) else: - content = make_unicode(subprocess.check_output(program_name, shell=(not list_like(program_name)), stdin=input_file, universal_newlines=True, timeout=timeout)) + content = make_unicode( + subprocess.check_output( + program_name, + shell=(not list_like(program_name)), + stdin=input_file, + universal_newlines=True, + timeout=timeout, + ) + ) input_file.seek(0) cls.__compare_two(program_name, content, std, grader)