# File overload.py (which will be inlined instead of imprted)
__all__ = [
'overloaded_functions',
'overload',
'NotSupplied',
'not_supplied',
'OverloadedFunctionNotFound',
'get_args_as_dict'
]
from collections import defaultdict
from typing import Any, Type
overloaded_functions = defaultdict(list)
def overload(classname: str=None):
"""A decorator to overload functions and methods.
For a method specify its class name as the classname argument."""
def wrapper(f):
name = f.__name__ if not classname else f'{classname}.{f.__name__}'
overloaded_functions[name].append(f)
return f
return wrapper
class NotSupplied:
pass
not_supplied = NotSupplied() # To distinguish arguments that are not supplied
class OverloadedFunctionNotFound(RuntimeError):
def __init__(self):
super().__init__('No overloaded function could be found for the arguments passed.')
def get_args_as_dict(
arg_specs: list[list[tuple[int, str, Type]]],
args: list[Any],
kwargs: dict[str, Any]
) -> dict[str, Any]:
"""When overloaded arguments vary by names or types this function will return a dictionary
with the actual argument names and values found.
The input is a list. Each element of the list is a list of tuples where
each tuple specifies an alternate possibility. The tuple specifies what position
in the argument list this argument should be found if its positional, the name of the
argument and the type of the argument.
For example: args_specs is [[(1, 'sku', str), (1, 'id', int)]]
The functions input arguments should have either an sku argument of type str
or an id argument of type int. If the argument is not a keyword argument then
we look at the type of the positional argument at index 1 for both alternates and select
the correct alternate based on type. Clearly, we cannot have two signatures that are identical
except for the argument name varying unless that argument is defined as keyword only.
"""
args_dict = {}
found_kwarg = False
n_args = len(args)
for arg_spec_list in arg_specs:
for position, arg_name, cls in arg_spec_list:
found_type = False
if arg_name in kwargs and isinstance(kwargs[arg_name], cls):
found_type = True
break
if position < n_args and isinstance(args[position], cls):
args_dict[arg_name] = args[position]
found_type = True
break
if not found_type:
raise OverloadedFunctionNotFound()
args_dict.update(kwargs)
return args_dict
######################################################
# File main.py
from typing import Any
#from overload import *
# Some examples of how we can determine which overloaded function to call:
# The OP's example:
from enum import Enum
class Format(Enum):
_invalid = 0
CD = 1
Vynil = 2
class Artist:
def __init__(self, id):
self.id = id
@classmethod
def fromId(cls, db, artistId):
return cls(artistId)
class SomeDbAbstraction:
def run_query(self, query : str) -> dict[str,Any]:
print('Query executed:', query)
return {
'sku': 'Some SKU',
'title': 'Some Title',
'artist': Artist(3),
'description': 'Some description',
'format': Format.CD,
'price': 10.00,
'id': 999
}
def get_insert_id(self) -> int:
return 1
class Product:
# use a dict instead of slots for an easy
# __repr__ implementation
"""
__slots__ = (
"__id",
"sku",
"title",
"artist",
"description",
"format",
"price"
)
"""
def __repr__(self):
return str(self.__dict__)
@overload('Product')
def __init__(
self,
db : SomeDbAbstraction,
sku : str,
title : str,
artist : Artist,
description : str,
format : Format,
price : float
) -> None:
print('__init__ #0')
db.run_query(f"""
INSERT INTO
Product
SET
sku = '{sku}',
title = {title},
artist = {artist.id},
description = {description},
format = {format.name},
price = {price}
;
""")
self.__id = db.get_insert_id()
self.sku = title
self.title = title
self.artist = artist
self.description = description
self.format = format
self.price = price
@overload('Product')
def __init__(self, db : SomeDbAbstraction, id : int):
print('__init__ #1')
data : dict[str,Any] = db.run_query(f"SELECT * FROM Product WHERE id = {id};")
self.__id = data.get("id")
self.sku = data.get("title")
self.title = data.get("title")
self.artist = Artist.fromId(db, data.get("artist"))
self.description = data.get("description")
self.format = Format(data.get("format"))
self.price = data.get("price")
@overload('Product')
def __init__(self, db : SomeDbAbstraction, sku : str):
print('__init__ #2')
data : dict[str,Any] = db.run_query(f"SELECT * FROM Product WHERE sku = '{sku}';")
self.__id = data.get("id")
self.sku = data.get("title")
self.title = data.get("title")
self.artist = Artist.fromId(db, data.get("artist"))
self.description = data.get("description")
self.format = Format(data.get("format"))
self.price = data.get("price")
@overload('Product')
def __init__(
self,
db : SomeDbAbstraction,
sku : str,
title : str,
artistId : int,
description : str,
format : Format,
price : float
) -> None:
print('__init__ #3')
db.run_query(f"""
INSERT INTO
Product
SET
sku = '{sku}',
title = {title},
artist = {artistId},
description = {description},
format = {format.name},
price = {price}
;
""")
self.__id = db.get_insert_id()
self.sku = title
self.title = title
self.artist = Artist.fromId(db, artistId)
self.description = description
self.format = format
self.price = price
def __init__(self, *args, **kwargs):
"""The dispatcher."""
n_args = len(args)
n_kwargs = len(kwargs)
total_args = n_args + n_kwargs
if total_args == 2:
# __init__ 1 or __init__ 2 according to whether the keyword arguments
# contain `id` or `sku` with the expected type or if the 2nd positional
# argument is one of the expected types:
d = get_args_as_dict([[(1, 'id', int), (1, 'sku', str)]], args, kwargs)
idx = 1 if 'id' in d else 2
elif total_args == 7:
# __init__ 0 or __init__ 3 according to whether the keyword arguments
# contain `artist` or `artistId` with the expected type or if the 4th positional
# argument is one of the expected types:
d = get_args_as_dict([[(3, 'artist', Artist), (3, 'artistId', int)]], args, kwargs)
idx = 0 if 'artist' in d else 3
else:
raise OverloadedFunctionNotFound()
overloaded_functions['Product.__init__'][idx](self, *args, **kwargs)
print('Example A')
print(Product(
SomeDbAbstraction(),
'some sku',
'some title',
Artist(7),
'some description',
Format.CD,
10.00), end='\n\n')
print('Example B')
print(Product(
SomeDbAbstraction(),
'some sku',
'some title',
9,
'some description',
Format.CD,
10.00), end='\n\n')
print('Example C')
print(Product(SomeDbAbstraction(), id=17), end='\n\n')
print('Example D')
print(Product(SomeDbAbstraction(), 'some sku'), end='\n\n')
###############################################################################
# Other examples:
# Example 1: Here one of the two overloaded functions takes an extra argument:
# This could have been done without overloading by specifying a default
# value for the second argument:
from math import log
@overload()
def my_log_fn(n: int | float) -> float:
"""Return log(n, 10)."""
return log(n, 10)
@overload()
def my_log_fn(n: int | float, base: int) -> float:
"""Return log(n, base)."""
return log(n, base)
def my_log_fn(n: int | float, base: int | NotSupplied=not_supplied) -> float:
if base is not_supplied:
return overloaded_functions['my_log_fn'][0](n)
return overloaded_functions['my_log_fn'][1](n, base)
print('Example 1:', my_log_fn(1_000_000), my_log_fn(1_000_000, 1_000))
###############################################################################
# Example 2: We can distinguish which overloaded function to call based on
# the number of arguments. This is easy if we accept only
# positional arguments or only keyword arguments:
@overload()
def add_to(s: set, value: object, /) -> None:
"""Add a value to a set."""
s.add(value)
@overload()
def add_to(d: dict, key: object, value: object, /) -> None:
"""Add a value to a dictionary."""
d[key] = value
# The actual implementation
def add_to(*args) -> None:
"""Decide which overloaded function to call based on number of arguments."""
n_args = len(args)
if n_args == 2:
return overloaded_functions['add_to'][0](*args)
if n_args == 3:
return overloaded_functions['add_to'][1](*args)
raise OverloadedFunctionNotFound()
d = {}
s = set()
add_to(d, 'a', 1)
add_to(s, 2)
print('Example 2:', f'd = {d}, s = {s}')
###############################################################################
# Example 3: We can distinguish which overloaded function to call based on
# the arguments types. This is easy when the signatures
# of the overloaded functions are identical except for
# the types:
@overload()
def foo(a: int, b: int) -> int:
"""Divide a by b and return result."""
return a // b
@overload()
def foo(a: float, b: float) -> float:
"""Divide a by b and return result."""
return a / b
def foo(a: int | float, b: int | float) -> int | float:
if isinstance(a, int) and isinstance(b, int):
return overloaded_functions['foo'][0](a, b)
# We will not require both a and b to be floats
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
return overloaded_functions['foo'][1](float(a), float(b))
raise OverloadedFunctionNotFound()
print('Example 3:', foo(7, 2), foo(7.0, 2.0))
###############################################################################
# Example 4: Same as Example 3 but with a class:
class TestClass:
@overload('TestClass')
def foo(self, a: int, b: int) -> int:
"""Divide a by b and return result."""
return a // b
@overload('TestClass')
def foo(self, a: float, b: float) -> float:
"""Divide a by b and return result."""
return a / b
def foo(self, a: int | float, b: int | float) -> int | float:
if isinstance(a, int) and isinstance(b, int):
return overloaded_functions['TestClass.foo'][0](self, a, b)
# We will not require both a and b to be floats
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
return overloaded_functions['TestClass.foo'][1](self, float(a), float(b))
raise OverloadedFunctionNotFound()
test_class = TestClass()
print('Example 4:', test_class.foo(7, 2), test_class.foo(7.0, 2.0))
IyBGaWxlIG92ZXJsb2FkLnB5ICh3aGljaCB3aWxsIGJlIGlubGluZWQgaW5zdGVhZCBvZiBpbXBydGVkKQoKX19hbGxfXyA9IFsKICAgICdvdmVybG9hZGVkX2Z1bmN0aW9ucycsCiAgICAnb3ZlcmxvYWQnLAogICAgJ05vdFN1cHBsaWVkJywKICAgICdub3Rfc3VwcGxpZWQnLAogICAgJ092ZXJsb2FkZWRGdW5jdGlvbk5vdEZvdW5kJywKICAgICdnZXRfYXJnc19hc19kaWN0JwpdCgpmcm9tIGNvbGxlY3Rpb25zIGltcG9ydCBkZWZhdWx0ZGljdApmcm9tIHR5cGluZyBpbXBvcnQgQW55LCBUeXBlCgpvdmVybG9hZGVkX2Z1bmN0aW9ucyA9IGRlZmF1bHRkaWN0KGxpc3QpCgpkZWYgb3ZlcmxvYWQoY2xhc3NuYW1lOiBzdHI9Tm9uZSk6CiAgICAiIiJBIGRlY29yYXRvciB0byBvdmVybG9hZCBmdW5jdGlvbnMgYW5kIG1ldGhvZHMuCiAgICBGb3IgYSBtZXRob2Qgc3BlY2lmeSBpdHMgY2xhc3MgbmFtZSBhcyB0aGUgY2xhc3NuYW1lIGFyZ3VtZW50LiIiIgogICAgZGVmIHdyYXBwZXIoZik6CiAgICAgICAgbmFtZSA9IGYuX19uYW1lX18gaWYgbm90IGNsYXNzbmFtZSBlbHNlIGYne2NsYXNzbmFtZX0ue2YuX19uYW1lX199JwogICAgICAgIG92ZXJsb2FkZWRfZnVuY3Rpb25zW25hbWVdLmFwcGVuZChmKQogICAgICAgIHJldHVybiBmCiAgICByZXR1cm4gd3JhcHBlcgoKY2xhc3MgTm90U3VwcGxpZWQ6CiAgICBwYXNzCgpub3Rfc3VwcGxpZWQgPSBOb3RTdXBwbGllZCgpICAjIFRvIGRpc3Rpbmd1aXNoIGFyZ3VtZW50cyB0aGF0IGFyZSBub3Qgc3VwcGxpZWQKCmNsYXNzIE92ZXJsb2FkZWRGdW5jdGlvbk5vdEZvdW5kKFJ1bnRpbWVFcnJvcik6CiAgICBkZWYgX19pbml0X18oc2VsZik6CiAgICAgICAgc3VwZXIoKS5fX2luaXRfXygnTm8gb3ZlcmxvYWRlZCBmdW5jdGlvbiBjb3VsZCBiZSBmb3VuZCBmb3IgdGhlIGFyZ3VtZW50cyBwYXNzZWQuJykKCmRlZiBnZXRfYXJnc19hc19kaWN0KAogICAgYXJnX3NwZWNzOiBsaXN0W2xpc3RbdHVwbGVbaW50LCBzdHIsIFR5cGVdXV0sCiAgICBhcmdzOiBsaXN0W0FueV0sCiAgICBrd2FyZ3M6IGRpY3Rbc3RyLCBBbnldCiAgICApIC0+IGRpY3Rbc3RyLCBBbnldOgoKICAgICIiIldoZW4gb3ZlcmxvYWRlZCBhcmd1bWVudHMgdmFyeSBieSBuYW1lcyBvciB0eXBlcyB0aGlzIGZ1bmN0aW9uIHdpbGwgcmV0dXJuIGEgZGljdGlvbmFyeQogICAgd2l0aCB0aGUgYWN0dWFsIGFyZ3VtZW50IG5hbWVzIGFuZCB2YWx1ZXMgZm91bmQuCgogICAgVGhlIGlucHV0IGlzIGEgbGlzdC4gRWFjaCBlbGVtZW50IG9mIHRoZSBsaXN0IGlzIGEgbGlzdCBvZiB0dXBsZXMgd2hlcmUKICAgIGVhY2ggdHVwbGUgc3BlY2lmaWVzIGFuIGFsdGVybmF0ZSBwb3NzaWJpbGl0eS4gVGhlIHR1cGxlIHNwZWNpZmllcyB3aGF0IHBvc2l0aW9uCiAgICBpbiB0aGUgYXJndW1lbnQgbGlzdCB0aGlzIGFyZ3VtZW50IHNob3VsZCBiZSBmb3VuZCBpZiBpdHMgcG9zaXRpb25hbCwgdGhlIG5hbWUgb2YgdGhlCiAgICBhcmd1bWVudCBhbmQgdGhlIHR5cGUgb2YgdGhlIGFyZ3VtZW50LgoKICAgIEZvciBleGFtcGxlOiBhcmdzX3NwZWNzIGlzIFtbKDEsICdza3UnLCBzdHIpLCAoMSwgJ2lkJywgaW50KV1dCiAgICBUaGUgZnVuY3Rpb25zIGlucHV0IGFyZ3VtZW50cyBzaG91bGQgaGF2ZSBlaXRoZXIgYW4gc2t1IGFyZ3VtZW50IG9mIHR5cGUgc3RyCiAgICBvciBhbiBpZCBhcmd1bWVudCBvZiB0eXBlIGludC4gSWYgdGhlIGFyZ3VtZW50IGlzIG5vdCBhIGtleXdvcmQgYXJndW1lbnQgdGhlbgogICAgd2UgbG9vayBhdCB0aGUgdHlwZSBvZiB0aGUgcG9zaXRpb25hbCBhcmd1bWVudCBhdCBpbmRleCAxIGZvciBib3RoIGFsdGVybmF0ZXMgYW5kIHNlbGVjdAogICAgdGhlIGNvcnJlY3QgYWx0ZXJuYXRlIGJhc2VkIG9uIHR5cGUuIENsZWFybHksIHdlIGNhbm5vdCBoYXZlIHR3byBzaWduYXR1cmVzIHRoYXQgYXJlIGlkZW50aWNhbAogICAgZXhjZXB0IGZvciB0aGUgYXJndW1lbnQgbmFtZSB2YXJ5aW5nIHVubGVzcyB0aGF0IGFyZ3VtZW50IGlzIGRlZmluZWQgYXMga2V5d29yZCBvbmx5LgogICAgIiIiCgogICAgYXJnc19kaWN0ID0ge30KICAgIGZvdW5kX2t3YXJnID0gRmFsc2UKICAgIG5fYXJncyA9IGxlbihhcmdzKQoKICAgIGZvciBhcmdfc3BlY19saXN0IGluIGFyZ19zcGVjczoKICAgICAgICBmb3IgcG9zaXRpb24sIGFyZ19uYW1lLCBjbHMgaW4gYXJnX3NwZWNfbGlzdDoKICAgICAgICAgICAgZm91bmRfdHlwZSA9IEZhbHNlCiAgICAgICAgICAgIGlmIGFyZ19uYW1lIGluIGt3YXJncyBhbmQgaXNpbnN0YW5jZShrd2FyZ3NbYXJnX25hbWVdLCBjbHMpOgogICAgICAgICAgICAgICAgZm91bmRfdHlwZSA9IFRydWUKICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgICAgIGlmIHBvc2l0aW9uIDwgbl9hcmdzIGFuZCBpc2luc3RhbmNlKGFyZ3NbcG9zaXRpb25dLCBjbHMpOgogICAgICAgICAgICAgICAgYXJnc19kaWN0W2FyZ19uYW1lXSA9IGFyZ3NbcG9zaXRpb25dCiAgICAgICAgICAgICAgICBmb3VuZF90eXBlID0gVHJ1ZQogICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICBpZiBub3QgZm91bmRfdHlwZToKICAgICAgICAgICAgcmFpc2UgT3ZlcmxvYWRlZEZ1bmN0aW9uTm90Rm91bmQoKQoKICAgIGFyZ3NfZGljdC51cGRhdGUoa3dhcmdzKQogICAgcmV0dXJuIGFyZ3NfZGljdAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIEZpbGUgbWFpbi5weQoKZnJvbSB0eXBpbmcgaW1wb3J0IEFueQoKI2Zyb20gb3ZlcmxvYWQgaW1wb3J0ICoKCiMgU29tZSBleGFtcGxlcyBvZiBob3cgd2UgY2FuIGRldGVybWluZSB3aGljaCBvdmVybG9hZGVkIGZ1bmN0aW9uIHRvIGNhbGw6CgojIFRoZSBPUCdzIGV4YW1wbGU6Cgpmcm9tIGVudW0gaW1wb3J0IEVudW0KCmNsYXNzIEZvcm1hdChFbnVtKToKICAgIF9pbnZhbGlkID0gMAogICAgQ0QgPSAxCiAgICBWeW5pbCA9IDIKCmNsYXNzIEFydGlzdDoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBpZCk6CiAgICAgICAgc2VsZi5pZCA9IGlkCgogICAgQGNsYXNzbWV0aG9kCiAgICBkZWYgZnJvbUlkKGNscywgZGIsIGFydGlzdElkKToKICAgICAgICByZXR1cm4gY2xzKGFydGlzdElkKQoKCmNsYXNzIFNvbWVEYkFic3RyYWN0aW9uOgogICAgZGVmIHJ1bl9xdWVyeShzZWxmLCBxdWVyeSA6IHN0cikgLT4gZGljdFtzdHIsQW55XToKICAgICAgICBwcmludCgnUXVlcnkgZXhlY3V0ZWQ6JywgcXVlcnkpCiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgJ3NrdSc6ICdTb21lIFNLVScsCiAgICAgICAgICAgICd0aXRsZSc6ICdTb21lIFRpdGxlJywKICAgICAgICAgICAgJ2FydGlzdCc6IEFydGlzdCgzKSwKICAgICAgICAgICAgJ2Rlc2NyaXB0aW9uJzogJ1NvbWUgZGVzY3JpcHRpb24nLAogICAgICAgICAgICAnZm9ybWF0JzogRm9ybWF0LkNELAogICAgICAgICAgICAncHJpY2UnOiAxMC4wMCwKICAgICAgICAgICAgJ2lkJzogOTk5CiAgICAgICAgfQoKICAgIGRlZiBnZXRfaW5zZXJ0X2lkKHNlbGYpIC0+IGludDoKICAgICAgICByZXR1cm4gMQoKY2xhc3MgUHJvZHVjdDoKICAgICMgdXNlIGEgZGljdCBpbnN0ZWFkIG9mIHNsb3RzIGZvciBhbiBlYXN5CiAgICAjIF9fcmVwcl9fIGltcGxlbWVudGF0aW9uCiAgICAiIiIKICAgIF9fc2xvdHNfXyA9ICgKICAgICAgICAiX19pZCIsCiAgICAgICAgInNrdSIsCiAgICAgICAgInRpdGxlIiwKICAgICAgICAiYXJ0aXN0IiwKICAgICAgICAiZGVzY3JpcHRpb24iLAogICAgICAgICJmb3JtYXQiLAogICAgICAgICJwcmljZSIKICAgICkKICAgICIiIgoKICAgIGRlZiBfX3JlcHJfXyhzZWxmKToKICAgICAgICByZXR1cm4gc3RyKHNlbGYuX19kaWN0X18pCgogICAgQG92ZXJsb2FkKCdQcm9kdWN0JykKICAgIGRlZiBfX2luaXRfXygKICAgICAgICBzZWxmLAogICAgICAgIGRiIDogU29tZURiQWJzdHJhY3Rpb24sCiAgICAgICAgc2t1IDogc3RyLAogICAgICAgIHRpdGxlIDogc3RyLAogICAgICAgIGFydGlzdCA6IEFydGlzdCwKICAgICAgICBkZXNjcmlwdGlvbiA6IHN0ciwKICAgICAgICBmb3JtYXQgOiBGb3JtYXQsCiAgICAgICAgcHJpY2UgOiBmbG9hdAogICAgKSAtPiBOb25lOgogICAgICAgIHByaW50KCdfX2luaXRfXyAjMCcpCiAgICAgICAgZGIucnVuX3F1ZXJ5KGYiIiIKICAgICAgICAgICAgSU5TRVJUIElOVE8KICAgICAgICAgICAgICAgIFByb2R1Y3QKICAgICAgICAgICAgU0VUCiAgICAgICAgICAgICAgICBza3UgPSAne3NrdX0nLAogICAgICAgICAgICAgICAgdGl0bGUgPSB7dGl0bGV9LAogICAgICAgICAgICAgICAgYXJ0aXN0ID0ge2FydGlzdC5pZH0sCiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbiA9IHtkZXNjcmlwdGlvbn0sCiAgICAgICAgICAgICAgICBmb3JtYXQgPSB7Zm9ybWF0Lm5hbWV9LAogICAgICAgICAgICAgICAgcHJpY2UgPSB7cHJpY2V9CiAgICAgICAgICAgIDsKICAgICAgICAiIiIpCiAgICAgICAgc2VsZi5fX2lkID0gZGIuZ2V0X2luc2VydF9pZCgpCiAgICAgICAgc2VsZi5za3UgPSB0aXRsZQogICAgICAgIHNlbGYudGl0bGUgPSB0aXRsZQogICAgICAgIHNlbGYuYXJ0aXN0ID0gYXJ0aXN0CiAgICAgICAgc2VsZi5kZXNjcmlwdGlvbiA9IGRlc2NyaXB0aW9uCiAgICAgICAgc2VsZi5mb3JtYXQgPSBmb3JtYXQKICAgICAgICBzZWxmLnByaWNlID0gcHJpY2UKCiAgICBAb3ZlcmxvYWQoJ1Byb2R1Y3QnKQogICAgZGVmIF9faW5pdF9fKHNlbGYsIGRiIDogU29tZURiQWJzdHJhY3Rpb24sIGlkIDogaW50KToKICAgICAgICBwcmludCgnX19pbml0X18gIzEnKQogICAgICAgIGRhdGEgOiBkaWN0W3N0cixBbnldID0gZGIucnVuX3F1ZXJ5KGYiU0VMRUNUICogRlJPTSBQcm9kdWN0IFdIRVJFIGlkID0ge2lkfTsiKQogICAgICAgIHNlbGYuX19pZCA9IGRhdGEuZ2V0KCJpZCIpCiAgICAgICAgc2VsZi5za3UgPSBkYXRhLmdldCgidGl0bGUiKQogICAgICAgIHNlbGYudGl0bGUgPSBkYXRhLmdldCgidGl0bGUiKQogICAgICAgIHNlbGYuYXJ0aXN0ID0gQXJ0aXN0LmZyb21JZChkYiwgZGF0YS5nZXQoImFydGlzdCIpKQogICAgICAgIHNlbGYuZGVzY3JpcHRpb24gPSBkYXRhLmdldCgiZGVzY3JpcHRpb24iKQogICAgICAgIHNlbGYuZm9ybWF0ID0gRm9ybWF0KGRhdGEuZ2V0KCJmb3JtYXQiKSkKICAgICAgICBzZWxmLnByaWNlID0gZGF0YS5nZXQoInByaWNlIikKCiAgICBAb3ZlcmxvYWQoJ1Byb2R1Y3QnKQogICAgZGVmIF9faW5pdF9fKHNlbGYsIGRiIDogU29tZURiQWJzdHJhY3Rpb24sIHNrdSA6IHN0cik6CiAgICAgICAgcHJpbnQoJ19faW5pdF9fICMyJykKICAgICAgICBkYXRhIDogZGljdFtzdHIsQW55XSA9IGRiLnJ1bl9xdWVyeShmIlNFTEVDVCAqIEZST00gUHJvZHVjdCBXSEVSRSBza3UgPSAne3NrdX0nOyIpCiAgICAgICAgc2VsZi5fX2lkID0gZGF0YS5nZXQoImlkIikKICAgICAgICBzZWxmLnNrdSA9IGRhdGEuZ2V0KCJ0aXRsZSIpCiAgICAgICAgc2VsZi50aXRsZSA9IGRhdGEuZ2V0KCJ0aXRsZSIpCiAgICAgICAgc2VsZi5hcnRpc3QgPSBBcnRpc3QuZnJvbUlkKGRiLCBkYXRhLmdldCgiYXJ0aXN0IikpCiAgICAgICAgc2VsZi5kZXNjcmlwdGlvbiA9IGRhdGEuZ2V0KCJkZXNjcmlwdGlvbiIpCiAgICAgICAgc2VsZi5mb3JtYXQgPSBGb3JtYXQoZGF0YS5nZXQoImZvcm1hdCIpKQogICAgICAgIHNlbGYucHJpY2UgPSBkYXRhLmdldCgicHJpY2UiKQoKICAgIEBvdmVybG9hZCgnUHJvZHVjdCcpCiAgICBkZWYgX19pbml0X18oCiAgICAgICAgc2VsZiwKICAgICAgICBkYiA6IFNvbWVEYkFic3RyYWN0aW9uLAogICAgICAgIHNrdSA6IHN0ciwKICAgICAgICB0aXRsZSA6IHN0ciwKICAgICAgICBhcnRpc3RJZCA6IGludCwKICAgICAgICBkZXNjcmlwdGlvbiA6IHN0ciwKICAgICAgICBmb3JtYXQgOiBGb3JtYXQsCiAgICAgICAgcHJpY2UgOiBmbG9hdAogICAgKSAtPiBOb25lOgogICAgICAgIHByaW50KCdfX2luaXRfXyAjMycpCiAgICAgICAgZGIucnVuX3F1ZXJ5KGYiIiIKICAgICAgICAgICAgSU5TRVJUIElOVE8KICAgICAgICAgICAgICAgIFByb2R1Y3QKICAgICAgICAgICAgU0VUCiAgICAgICAgICAgICAgICBza3UgPSAne3NrdX0nLAogICAgICAgICAgICAgICAgdGl0bGUgPSB7dGl0bGV9LAogICAgICAgICAgICAgICAgYXJ0aXN0ID0ge2FydGlzdElkfSwKICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uID0ge2Rlc2NyaXB0aW9ufSwKICAgICAgICAgICAgICAgIGZvcm1hdCA9IHtmb3JtYXQubmFtZX0sCiAgICAgICAgICAgICAgICBwcmljZSA9IHtwcmljZX0KICAgICAgICAgICAgOwogICAgICAgICIiIikKICAgICAgICBzZWxmLl9faWQgPSBkYi5nZXRfaW5zZXJ0X2lkKCkKICAgICAgICBzZWxmLnNrdSA9IHRpdGxlCiAgICAgICAgc2VsZi50aXRsZSA9IHRpdGxlCiAgICAgICAgc2VsZi5hcnRpc3QgPSBBcnRpc3QuZnJvbUlkKGRiLCBhcnRpc3RJZCkKICAgICAgICBzZWxmLmRlc2NyaXB0aW9uID0gZGVzY3JpcHRpb24KICAgICAgICBzZWxmLmZvcm1hdCA9IGZvcm1hdAogICAgICAgIHNlbGYucHJpY2UgPSBwcmljZQoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCAqYXJncywgKiprd2FyZ3MpOgogICAgICAgICIiIlRoZSBkaXNwYXRjaGVyLiIiIgogICAgICAgIG5fYXJncyA9IGxlbihhcmdzKQogICAgICAgIG5fa3dhcmdzID0gbGVuKGt3YXJncykKICAgICAgICB0b3RhbF9hcmdzID0gbl9hcmdzICsgbl9rd2FyZ3MKCiAgICAgICAgaWYgdG90YWxfYXJncyA9PSAyOgogICAgICAgICAgICAjIF9faW5pdF9fIDEgb3IgX19pbml0X18gMiBhY2NvcmRpbmcgdG8gd2hldGhlciB0aGUga2V5d29yZCBhcmd1bWVudHMKICAgICAgICAgICAgIyBjb250YWluIGBpZGAgb3IgYHNrdWAgd2l0aCB0aGUgZXhwZWN0ZWQgdHlwZSBvciBpZiB0aGUgMm5kIHBvc2l0aW9uYWwKICAgICAgICAgICAgIyBhcmd1bWVudCBpcyBvbmUgb2YgdGhlIGV4cGVjdGVkIHR5cGVzOgogICAgICAgICAgICBkID0gZ2V0X2FyZ3NfYXNfZGljdChbWygxLCAnaWQnLCBpbnQpLCAoMSwgJ3NrdScsIHN0cildXSwgYXJncywga3dhcmdzKQogICAgICAgICAgICBpZHggPSAxIGlmICdpZCcgaW4gZCBlbHNlIDIKICAgICAgICBlbGlmIHRvdGFsX2FyZ3MgPT0gNzoKICAgICAgICAgICAgIyBfX2luaXRfXyAwIG9yIF9faW5pdF9fIDMgYWNjb3JkaW5nIHRvIHdoZXRoZXIgdGhlIGtleXdvcmQgYXJndW1lbnRzCiAgICAgICAgICAgICMgY29udGFpbiBgYXJ0aXN0YCBvciBgYXJ0aXN0SWRgIHdpdGggdGhlIGV4cGVjdGVkIHR5cGUgb3IgaWYgdGhlIDR0aCBwb3NpdGlvbmFsCiAgICAgICAgICAgICMgYXJndW1lbnQgaXMgb25lIG9mIHRoZSBleHBlY3RlZCB0eXBlczoKICAgICAgICAgICAgZCA9IGdldF9hcmdzX2FzX2RpY3QoW1soMywgJ2FydGlzdCcsIEFydGlzdCksICgzLCAnYXJ0aXN0SWQnLCBpbnQpXV0sIGFyZ3MsIGt3YXJncykKICAgICAgICAgICAgaWR4ID0gMCBpZiAnYXJ0aXN0JyBpbiBkIGVsc2UgMwogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHJhaXNlIE92ZXJsb2FkZWRGdW5jdGlvbk5vdEZvdW5kKCkKCiAgICAgICAgb3ZlcmxvYWRlZF9mdW5jdGlvbnNbJ1Byb2R1Y3QuX19pbml0X18nXVtpZHhdKHNlbGYsICphcmdzLCAqKmt3YXJncykKCnByaW50KCdFeGFtcGxlIEEnKQpwcmludChQcm9kdWN0KAogICAgU29tZURiQWJzdHJhY3Rpb24oKSwKICAgICdzb21lIHNrdScsCiAgICAnc29tZSB0aXRsZScsCiAgICBBcnRpc3QoNyksCiAgICAnc29tZSBkZXNjcmlwdGlvbicsCiAgICBGb3JtYXQuQ0QsCiAgICAxMC4wMCksIGVuZD0nXG5cbicpCgpwcmludCgnRXhhbXBsZSBCJykKcHJpbnQoUHJvZHVjdCgKICAgIFNvbWVEYkFic3RyYWN0aW9uKCksCiAgICAnc29tZSBza3UnLAogICAgJ3NvbWUgdGl0bGUnLAogICAgOSwKICAgICdzb21lIGRlc2NyaXB0aW9uJywKICAgIEZvcm1hdC5DRCwKICAgIDEwLjAwKSwgZW5kPSdcblxuJykKCnByaW50KCdFeGFtcGxlIEMnKQpwcmludChQcm9kdWN0KFNvbWVEYkFic3RyYWN0aW9uKCksIGlkPTE3KSwgZW5kPSdcblxuJykKCnByaW50KCdFeGFtcGxlIEQnKQpwcmludChQcm9kdWN0KFNvbWVEYkFic3RyYWN0aW9uKCksICdzb21lIHNrdScpLCBlbmQ9J1xuXG4nKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBPdGhlciBleGFtcGxlczoKCiMgRXhhbXBsZSAxOiBIZXJlIG9uZSBvZiB0aGUgdHdvIG92ZXJsb2FkZWQgZnVuY3Rpb25zIHRha2VzIGFuIGV4dHJhIGFyZ3VtZW50OgojIFRoaXMgY291bGQgaGF2ZSBiZWVuIGRvbmUgd2l0aG91dCBvdmVybG9hZGluZyBieSBzcGVjaWZ5aW5nIGEgZGVmYXVsdAojIHZhbHVlIGZvciB0aGUgc2Vjb25kIGFyZ3VtZW50OgoKZnJvbSBtYXRoIGltcG9ydCBsb2cKCkBvdmVybG9hZCgpCmRlZiBteV9sb2dfZm4objogaW50IHwgZmxvYXQpIC0+IGZsb2F0OgogICAgIiIiUmV0dXJuIGxvZyhuLCAxMCkuIiIiCgogICAgcmV0dXJuIGxvZyhuLCAxMCkKCkBvdmVybG9hZCgpCmRlZiBteV9sb2dfZm4objogaW50IHwgZmxvYXQsIGJhc2U6IGludCkgLT4gZmxvYXQ6CiAgICAiIiJSZXR1cm4gbG9nKG4sIGJhc2UpLiIiIgoKICAgIHJldHVybiBsb2cobiwgYmFzZSkKCmRlZiBteV9sb2dfZm4objogaW50IHwgZmxvYXQsIGJhc2U6IGludCB8IE5vdFN1cHBsaWVkPW5vdF9zdXBwbGllZCkgLT4gZmxvYXQ6CiAgICBpZiBiYXNlIGlzIG5vdF9zdXBwbGllZDoKICAgICAgICByZXR1cm4gb3ZlcmxvYWRlZF9mdW5jdGlvbnNbJ215X2xvZ19mbiddWzBdKG4pCgogICAgcmV0dXJuIG92ZXJsb2FkZWRfZnVuY3Rpb25zWydteV9sb2dfZm4nXVsxXShuLCBiYXNlKQoKcHJpbnQoJ0V4YW1wbGUgMTonLCBteV9sb2dfZm4oMV8wMDBfMDAwKSwgbXlfbG9nX2ZuKDFfMDAwXzAwMCwgMV8wMDApKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBFeGFtcGxlIDI6IFdlIGNhbiBkaXN0aW5ndWlzaCB3aGljaCBvdmVybG9hZGVkIGZ1bmN0aW9uIHRvIGNhbGwgYmFzZWQgb24KIyB0aGUgbnVtYmVyIG9mIGFyZ3VtZW50cy4gVGhpcyBpcyBlYXN5IGlmIHdlIGFjY2VwdCBvbmx5CiMgcG9zaXRpb25hbCBhcmd1bWVudHMgb3Igb25seSBrZXl3b3JkIGFyZ3VtZW50czoKCkBvdmVybG9hZCgpCmRlZiBhZGRfdG8oczogc2V0LCB2YWx1ZTogb2JqZWN0LCAvKSAtPiBOb25lOgogICAgIiIiQWRkIGEgdmFsdWUgdG8gYSBzZXQuIiIiCiAgICBzLmFkZCh2YWx1ZSkKCkBvdmVybG9hZCgpCmRlZiBhZGRfdG8oZDogZGljdCwga2V5OiBvYmplY3QsIHZhbHVlOiBvYmplY3QsIC8pIC0+IE5vbmU6CiAgICAiIiJBZGQgYSB2YWx1ZSB0byBhIGRpY3Rpb25hcnkuIiIiCiAgICBkW2tleV0gPSB2YWx1ZQoKIyBUaGUgYWN0dWFsIGltcGxlbWVudGF0aW9uCmRlZiBhZGRfdG8oKmFyZ3MpIC0+IE5vbmU6CiAgICAiIiJEZWNpZGUgd2hpY2ggb3ZlcmxvYWRlZCBmdW5jdGlvbiB0byBjYWxsIGJhc2VkIG9uIG51bWJlciBvZiBhcmd1bWVudHMuIiIiCgogICAgbl9hcmdzID0gbGVuKGFyZ3MpCgogICAgaWYgbl9hcmdzID09IDI6CiAgICAgICAgcmV0dXJuIG92ZXJsb2FkZWRfZnVuY3Rpb25zWydhZGRfdG8nXVswXSgqYXJncykKCiAgICBpZiBuX2FyZ3MgPT0gMzoKICAgICAgICByZXR1cm4gb3ZlcmxvYWRlZF9mdW5jdGlvbnNbJ2FkZF90byddWzFdKCphcmdzKQoKICAgIHJhaXNlIE92ZXJsb2FkZWRGdW5jdGlvbk5vdEZvdW5kKCkKCgpkID0ge30KcyA9IHNldCgpCgphZGRfdG8oZCwgJ2EnLCAxKQphZGRfdG8ocywgMikKCnByaW50KCdFeGFtcGxlIDI6JywgZidkID0ge2R9LCBzID0ge3N9JykKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgRXhhbXBsZSAzOiBXZSBjYW4gZGlzdGluZ3Vpc2ggd2hpY2ggb3ZlcmxvYWRlZCBmdW5jdGlvbiB0byBjYWxsIGJhc2VkIG9uCiMgdGhlIGFyZ3VtZW50cyB0eXBlcy4gVGhpcyBpcyBlYXN5IHdoZW4gdGhlIHNpZ25hdHVyZXMKIyBvZiB0aGUgb3ZlcmxvYWRlZCBmdW5jdGlvbnMgYXJlIGlkZW50aWNhbCBleGNlcHQgZm9yCiMgdGhlIHR5cGVzOgoKQG92ZXJsb2FkKCkKZGVmIGZvbyhhOiBpbnQsIGI6IGludCkgLT4gaW50OgogICAgIiIiRGl2aWRlIGEgYnkgYiBhbmQgcmV0dXJuIHJlc3VsdC4iIiIKCiAgICByZXR1cm4gYSAvLyBiCgpAb3ZlcmxvYWQoKQpkZWYgZm9vKGE6IGZsb2F0LCBiOiBmbG9hdCkgLT4gZmxvYXQ6CiAgICAiIiJEaXZpZGUgYSBieSBiIGFuZCByZXR1cm4gcmVzdWx0LiIiIgoKICAgIHJldHVybiBhIC8gYgoKZGVmIGZvbyhhOiBpbnQgfCBmbG9hdCwgYjogaW50IHwgZmxvYXQpIC0+IGludCB8IGZsb2F0OgogICAgaWYgaXNpbnN0YW5jZShhLCBpbnQpIGFuZCBpc2luc3RhbmNlKGIsIGludCk6CiAgICAgICAgcmV0dXJuIG92ZXJsb2FkZWRfZnVuY3Rpb25zWydmb28nXVswXShhLCBiKQoKICAgICMgV2Ugd2lsbCBub3QgcmVxdWlyZSBib3RoIGEgYW5kIGIgdG8gYmUgZmxvYXRzCiAgICBpZiBpc2luc3RhbmNlKGEsIChpbnQsIGZsb2F0KSkgYW5kIGlzaW5zdGFuY2UoYiwgKGludCwgZmxvYXQpKToKICAgICAgICByZXR1cm4gb3ZlcmxvYWRlZF9mdW5jdGlvbnNbJ2ZvbyddWzFdKGZsb2F0KGEpLCBmbG9hdChiKSkKCiAgICByYWlzZSBPdmVybG9hZGVkRnVuY3Rpb25Ob3RGb3VuZCgpCgpwcmludCgnRXhhbXBsZSAzOicsIGZvbyg3LCAyKSwgZm9vKDcuMCwgMi4wKSkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgRXhhbXBsZSA0OiBTYW1lIGFzIEV4YW1wbGUgMyBidXQgd2l0aCBhIGNsYXNzOgoKY2xhc3MgVGVzdENsYXNzOgogICAgQG92ZXJsb2FkKCdUZXN0Q2xhc3MnKQogICAgZGVmIGZvbyhzZWxmLCBhOiBpbnQsIGI6IGludCkgLT4gaW50OgogICAgICAgICIiIkRpdmlkZSBhIGJ5IGIgYW5kIHJldHVybiByZXN1bHQuIiIiCgogICAgICAgIHJldHVybiBhIC8vIGIKCiAgICBAb3ZlcmxvYWQoJ1Rlc3RDbGFzcycpCiAgICBkZWYgZm9vKHNlbGYsIGE6IGZsb2F0LCBiOiBmbG9hdCkgLT4gZmxvYXQ6CiAgICAgICAgIiIiRGl2aWRlIGEgYnkgYiBhbmQgcmV0dXJuIHJlc3VsdC4iIiIKCiAgICAgICAgcmV0dXJuIGEgLyBiCgogICAgZGVmIGZvbyhzZWxmLCBhOiBpbnQgfCBmbG9hdCwgYjogaW50IHwgZmxvYXQpIC0+IGludCB8IGZsb2F0OgogICAgICAgIGlmIGlzaW5zdGFuY2UoYSwgaW50KSBhbmQgaXNpbnN0YW5jZShiLCBpbnQpOgogICAgICAgICAgICByZXR1cm4gb3ZlcmxvYWRlZF9mdW5jdGlvbnNbJ1Rlc3RDbGFzcy5mb28nXVswXShzZWxmLCBhLCBiKQoKICAgICAgICAjIFdlIHdpbGwgbm90IHJlcXVpcmUgYm90aCBhIGFuZCBiIHRvIGJlIGZsb2F0cwogICAgICAgIGlmIGlzaW5zdGFuY2UoYSwgKGludCwgZmxvYXQpKSBhbmQgaXNpbnN0YW5jZShiLCAoaW50LCBmbG9hdCkpOgogICAgICAgICAgICByZXR1cm4gb3ZlcmxvYWRlZF9mdW5jdGlvbnNbJ1Rlc3RDbGFzcy5mb28nXVsxXShzZWxmLCBmbG9hdChhKSwgZmxvYXQoYikpCgogICAgICAgIHJhaXNlIE92ZXJsb2FkZWRGdW5jdGlvbk5vdEZvdW5kKCkKCnRlc3RfY2xhc3MgPSBUZXN0Q2xhc3MoKQpwcmludCgnRXhhbXBsZSA0OicsIHRlc3RfY2xhc3MuZm9vKDcsIDIpLCB0ZXN0X2NsYXNzLmZvbyg3LjAsIDIuMCkpCgo=