Add 'universe/' from commit '8ad51b24dd'
				
					
				
			git-subtree-dir: universe git-subtree-mainline:15110e6de9git-subtree-split:8ad51b24dd
This commit is contained in:
		
						commit
						fb9380ba26
					
				
					 131 changed files with 13792 additions and 0 deletions
				
			
		
							
								
								
									
										1
									
								
								universe/.envrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								universe/.envrc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | export NIX_PATH=nixpkgs=$HOME/.nix-defexpr/nixpkgs:depot=$HOME/depot:universe=$HOME/universe | ||||||
							
								
								
									
										7
									
								
								universe/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								universe/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | # Haskell | ||||||
|  | *.hi | ||||||
|  | *.o | ||||||
|  | 
 | ||||||
|  | # Python | ||||||
|  | __pycache__ | ||||||
|  | *.class | ||||||
							
								
								
									
										9
									
								
								universe/README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								universe/README.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | # Universe | ||||||
|  | 
 | ||||||
|  | This is my mono-repo. Having a personal mono-repo is a new idea for me, so at | ||||||
|  | the time of this writing, the state of this repository is fledgling. | ||||||
|  | 
 | ||||||
|  | I'm attempting to amass a collection of functions across a variety of languages | ||||||
|  | while minimizing the costs of sharing the code across a projects. Stay tuned for | ||||||
|  | more updates as my definition of the mono-repo becomes more clear, my opinions | ||||||
|  | evolve, and my preferences change. | ||||||
							
								
								
									
										378
									
								
								universe/ac_types/ac_types.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								universe/ac_types/ac_types.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,378 @@ | ||||||
|  | from itertools import product | ||||||
|  | import string | ||||||
|  | from pretty import pretty_print | ||||||
|  | import csv | ||||||
|  | import parse | ||||||
|  | import serialize | ||||||
|  | import dict | ||||||
|  | import scrape | ||||||
|  | import fs | ||||||
|  | import f | ||||||
|  | import log | ||||||
|  | import regex | ||||||
|  | import input_constant | ||||||
|  | from test_utils import simple_assert | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Main | ||||||
|  | ################################################################################ | ||||||
|  | enable_tests = True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # parse_csv :: Path -> [RowAsDict] | ||||||
|  | def parse_csv(path): | ||||||
|  |     parser = { | ||||||
|  |         "Name": parse.required("Name", parse.identity), | ||||||
|  |         "Type": parse.required("Type", parse.as_type), | ||||||
|  |         # TODO: Are we sure we want to default to False here? | ||||||
|  |         "Optional": parse.if_empty(False, parse.as_yes_no), | ||||||
|  |         # We'd like to skip rows where "Data Source Type" is empty. | ||||||
|  |         "Data Source Type": parse.nullable(parse.as_data_source_type), | ||||||
|  |         "Data Source/Value": parse.nullable(parse.identity), | ||||||
|  |     } | ||||||
|  |     result = [] | ||||||
|  |     # Below are only the column in which we're interested: | ||||||
|  |     columns = [ | ||||||
|  |         'Name', | ||||||
|  |         'Type', | ||||||
|  |         'Optional', | ||||||
|  |         'Data Source Type', | ||||||
|  |         'Data Source/Value', | ||||||
|  |     ] | ||||||
|  |     assert set(columns) == set(parser.keys()) | ||||||
|  |     with open(path, 'r') as f: | ||||||
|  |         reader = csv.DictReader(f) | ||||||
|  |         for row in reader: | ||||||
|  |             try: | ||||||
|  |                 parsed = parse.apply_parser(parser, row) | ||||||
|  |                 result.append(dict.take(columns, parsed)) | ||||||
|  |             except Exception as e: | ||||||
|  |                 pretty_print(row) | ||||||
|  |                 raise Exception('Failed parsing the following row: {}'.format( | ||||||
|  |                     ','.join(row.values()))) | ||||||
|  |                 return | ||||||
|  | 
 | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def serialize_id_value(xs): | ||||||
|  |     return string.indent('\n'.join([ | ||||||
|  |         'id: {}'.format(serialize.literal(xs['id'])), | ||||||
|  |         'value: {}'.format(serialize.literal(xs['value'])), | ||||||
|  |     ])) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # serialize_parent_routing_frd :: RowAsDict -> [String] | ||||||
|  | def serialize_parent_routing_frd(row): | ||||||
|  |     # All parent_routing_frds should be set as Hard-Code | ||||||
|  |     assert row['Data Source Type'] == 'Hard-Code' | ||||||
|  | 
 | ||||||
|  |     frds = { | ||||||
|  |         'Consult Type': '^88149', | ||||||
|  |         'neoenum.program': '^87379', | ||||||
|  |         'form.country': '^16296', | ||||||
|  |         'Form.ad_language': "^3906" | ||||||
|  |     } | ||||||
|  |     name, value = row['Name'], parse.as_union_type(row['Data Source/Value']) | ||||||
|  | 
 | ||||||
|  |     result = [] | ||||||
|  |     for x in value: | ||||||
|  |         header = 'parent_lookup_frds {' | ||||||
|  |         fields = serialize_id_value({'id': frds[name], 'value': x}) | ||||||
|  |         footer = '}' | ||||||
|  |         result.append('\n'.join([header, fields, footer])) | ||||||
|  | 
 | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = serialize_parent_routing_frd({ | ||||||
|  |     'Name': | ||||||
|  |     'neoenum.program', | ||||||
|  |     'Data Source Type': | ||||||
|  |     'Hard-Code', | ||||||
|  |     'Data Source/Value': | ||||||
|  |     '"olympus" or "olympus_plus"', | ||||||
|  | }) | ||||||
|  | expected = [ | ||||||
|  |     """parent_lookup_frds { | ||||||
|  |   id: "^87379" | ||||||
|  |   value: "olympus" | ||||||
|  | }""", | ||||||
|  |     """parent_lookup_frds { | ||||||
|  |   id: "^87379" | ||||||
|  |   value: "olympus_plus" | ||||||
|  | }""", | ||||||
|  | ] | ||||||
|  | simple_assert(actual, expected, name='serialize_parent_routing_frd') | ||||||
|  | 
 | ||||||
|  | actual = serialize_parent_routing_frd({ | ||||||
|  |     'Name': | ||||||
|  |     'Consult Type', | ||||||
|  |     'Type': | ||||||
|  |     'Parent Routing FRD', | ||||||
|  |     'AIV': | ||||||
|  |     False, | ||||||
|  |     'Optional': | ||||||
|  |     False, | ||||||
|  |     'Data Source Type': | ||||||
|  |     'Hard-Code', | ||||||
|  |     'Data Source/Value': | ||||||
|  |     'ads_accountappeals_autoconsult' | ||||||
|  | }) | ||||||
|  | expected = [ | ||||||
|  |     """parent_lookup_frds { | ||||||
|  |   id: "^88149" | ||||||
|  |   value: "ads_accountappeals_autoconsult" | ||||||
|  | }""" | ||||||
|  | ] | ||||||
|  | simple_assert(actual, expected, name='serialize_parent_routing_frd') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def serialize_return_routing_frd(row): | ||||||
|  |     header = 'parent_return_routing_frds {' | ||||||
|  |     fields = serialize_id_value({ | ||||||
|  |         'id': row['Name'], | ||||||
|  |         'value': row['Data Source/Value'], | ||||||
|  |     }) | ||||||
|  |     footer = '}' | ||||||
|  |     return '\n'.join([header, fields, footer]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def serialize_consult_routing_frd(row): | ||||||
|  |     header = 'consult_routing_frds {' | ||||||
|  |     fields = serialize_id_value({ | ||||||
|  |         'id': row['Name'], | ||||||
|  |         'value': row['Data Source/Value'], | ||||||
|  |     }) | ||||||
|  |     footer = '}' | ||||||
|  |     return '\n'.join([header, fields, footer]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # TODO: Reconcile this definition with serialize.input. | ||||||
|  | # serialize_inputs :: RowAsDict -> [String] | ||||||
|  | def serialize_input(row): | ||||||
|  |     value_type = row['Data Source Type'] | ||||||
|  |     name, value = string.trim_prefix('IDENTIFIER_', | ||||||
|  |                                      row['Name']), row['Data Source/Value'] | ||||||
|  | 
 | ||||||
|  |     if ' or ' in value and value_type != 'Hard-Code': | ||||||
|  |         log.warn('Found a union type in a non-"Hard-Code" row: {}'.format(row)) | ||||||
|  | 
 | ||||||
|  |     # TODO: We need to resolve row['Name'] into "^<id-number>". But only | ||||||
|  |     # Sometimes... so... when is that sometimes? | ||||||
|  |     if value_type == 'Hard-Code': | ||||||
|  |         return serialize.input( | ||||||
|  |             input_type='CONSTANT', | ||||||
|  |             fields={ | ||||||
|  |                 'consult_frd_id': name, | ||||||
|  |                 'is_optional': row['Optional'], | ||||||
|  |                 # TODO: Call resolution function. | ||||||
|  |                 'constant_value': input_constant.to_rule_id(value), | ||||||
|  |             }) | ||||||
|  |     elif value_type == 'Atlas': | ||||||
|  |         # We need to remove the trailing parens if they exist. See the CSVs for more | ||||||
|  |         # context. | ||||||
|  |         value = regex.remove(r'\s\([\w\s]+\)$', value) | ||||||
|  |         return serialize.input(input_type='SIGNAL', | ||||||
|  |                                fields={ | ||||||
|  |                                    'consult_frd_id': name, | ||||||
|  |                                    'is_optional': row['Optional'], | ||||||
|  |                                }) | ||||||
|  |     elif value_type == 'Form': | ||||||
|  |         # TODO: Prefer a generic serialize.dictionary | ||||||
|  |         # We need to remove the trailing parens if they exist. See the CSVs for more | ||||||
|  |         # context. | ||||||
|  |         value = regex.remove(r'\s\([\w\s]+\)$', value) | ||||||
|  |         return serialize.input(input_type='PARENT_FRD', | ||||||
|  |                                fields={ | ||||||
|  |                                    'consult_frd_id': | ||||||
|  |                                    name, | ||||||
|  |                                    'is_optional': | ||||||
|  |                                    row['Optional'], | ||||||
|  |                                    'parent_frd_id': | ||||||
|  |                                    scrape.attribute_id_for( | ||||||
|  |                                        value, error_when_absent=False), | ||||||
|  |                                }) | ||||||
|  |     else: | ||||||
|  |         raise Exception("This should not have occurred.") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # csv_to_proto :: Path -> [Protobuf] | ||||||
|  | def csv_to_proto(path): | ||||||
|  |     """Maps the CSV located at `path` into a textproto that Auto Consult will consume.""" | ||||||
|  |     # ORGANIZATION, which is currently a 'Consult Routing FRD' should become a | ||||||
|  |     # 'Consult Parameter' as "neo_organization". | ||||||
|  |     consult_routing_frds_blacklist = {'ORGANIZATION'} | ||||||
|  |     index = { | ||||||
|  |         'Parent Routing FRD': [], | ||||||
|  |         'Consult Parameter': [], | ||||||
|  |         'Return Routing FRD': [], | ||||||
|  |         'Consult Routing FRD': [], | ||||||
|  |         'Input': [] | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     # Index each row according to its "Type" column. | ||||||
|  |     for row in parse_csv(path): | ||||||
|  |         name, rtype, dst = row['Name'], row['Type'], row['Data Source Type'] | ||||||
|  |         # Here we need to mutate the spreadsheet because the curators encoded a | ||||||
|  |         # 'Consult Parameter', "ORGANIZATION", as a 'Consult Routing FRD'. | ||||||
|  |         if name == 'ORGANIZATION' and rtype == 'Consult Routing FRD': | ||||||
|  |             row['Type'] = 'Consult Parameter' | ||||||
|  |             index['Consult Parameter'].append(row) | ||||||
|  |             continue | ||||||
|  |         if dst is None: | ||||||
|  |             log.warn('Column "Data Source Type" is None. Skipping this row.') | ||||||
|  |             continue | ||||||
|  |         if dst == 'N/A': | ||||||
|  |             continue | ||||||
|  |         index[row['Type']].append(row) | ||||||
|  | 
 | ||||||
|  |     return serialize_index(index) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def serialize_consult_parameters(xs): | ||||||
|  |     result = [] | ||||||
|  |     transforms = { | ||||||
|  |         'Taxonomy ID': 'taxonomy_id', | ||||||
|  |         'View ID': 'view_id', | ||||||
|  |         'Timeout': 'max_wait_time_for_consult_secs', | ||||||
|  |         'Re-Route if Customer Responds': 'reroute_on_customer_interaction', | ||||||
|  |         'ORGANIZATION': 'neo_organization' | ||||||
|  |     } | ||||||
|  |     parsers = { | ||||||
|  |         'Taxonomy ID': | ||||||
|  |         parse.identity, | ||||||
|  |         'View ID': | ||||||
|  |         parse.identity, | ||||||
|  |         'Timeout': | ||||||
|  |         lambda x: parse.as_hours(x) * 60 * 60, | ||||||
|  |         'Re-Route if Customer Responds': | ||||||
|  |         parse.as_mapping({ | ||||||
|  |             'TRUE': True, | ||||||
|  |             'FALSE': False | ||||||
|  |         }), | ||||||
|  |         'ORGANIZATION': | ||||||
|  |         parse.identity | ||||||
|  |     } | ||||||
|  |     for row in xs: | ||||||
|  |         name = row['Name'] | ||||||
|  |         parser = parsers[name] | ||||||
|  |         key, value = transforms[name], parser(row['Data Source/Value']) | ||||||
|  |         result.append('  {}: {}'.format(key, serialize.literal(value))) | ||||||
|  | 
 | ||||||
|  |     return '\n'.join(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def serialize_index(index): | ||||||
|  |     header = 'consult_settings {' | ||||||
|  | 
 | ||||||
|  |     consult_parameters = serialize_consult_parameters( | ||||||
|  |         index['Consult Parameter']) | ||||||
|  | 
 | ||||||
|  |     product_xs = [] | ||||||
|  |     parent_lookup_frds = [] | ||||||
|  |     for row in index['Parent Routing FRD']: | ||||||
|  |         product_xs.append(serialize_parent_routing_frd(row)) | ||||||
|  |     for frds in product(*product_xs): | ||||||
|  |         parent_lookup_frds.append('\n'.join(frds)) | ||||||
|  | 
 | ||||||
|  |     # TODO: Cover with tests. | ||||||
|  |     parent_return_routing_frds = string.indent('\n'.join([ | ||||||
|  |         serialize_return_routing_frd(row) | ||||||
|  |         for row in index['Return Routing FRD'] | ||||||
|  |     ])) | ||||||
|  | 
 | ||||||
|  |     # TODO: Cover with tests. | ||||||
|  |     consult_routing_frds = string.indent('\n'.join([ | ||||||
|  |         serialize_consult_routing_frd(row) | ||||||
|  |         for row in index['Consult Routing FRD'] | ||||||
|  |     ])) | ||||||
|  | 
 | ||||||
|  |     inputs = string.indent('\n'.join( | ||||||
|  |         [serialize_input(row) for row in index['Input']])) | ||||||
|  | 
 | ||||||
|  |     footer = '}' | ||||||
|  | 
 | ||||||
|  |     result = [] | ||||||
|  |     for parent_frd in parent_lookup_frds: | ||||||
|  |         result.append('\n'.join([ | ||||||
|  |             header, consult_parameters, | ||||||
|  |             string.indent(parent_frd), parent_return_routing_frds, | ||||||
|  |             consult_routing_frds, inputs, footer | ||||||
|  |         ])) | ||||||
|  | 
 | ||||||
|  |     return '\n'.join(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | csv_directory = f.ensure_absolute("~/auto-consult-csv") | ||||||
|  | # TODO: Add missing files. | ||||||
|  | csvs = [ | ||||||
|  |     'Non-Sensitive Ads Review (Olympus).csv', | ||||||
|  |     'Ads Review (Olympus).csv', | ||||||
|  |     'Ad Review (Non-Olympus).csv', | ||||||
|  |     'Review Account Under Review (Olympus).csv', | ||||||
|  |     'Accounts Review Requests (Non-Olympus).csv', | ||||||
|  |     'Review Suspended Account (Olympus).csv', | ||||||
|  |     'Review Suspended Account (Non-Olympus).csv', | ||||||
|  |     'Suspended Long Form (Olympus).csv', | ||||||
|  |     'Suspended Long Form (Non-Olympus).csv', | ||||||
|  |     'Copyright (Olympus).csv', | ||||||
|  |     'Copyright (Non-Olympus).csv', | ||||||
|  |     'EU Election Certification (Olympus).csv', | ||||||
|  |     'EU Election Certification #2 (Olympus).csv', | ||||||
|  |     'EU Election Certification (Non-Olympus).csv', | ||||||
|  |     'EU Election Certification #2 (Non-Olympus).csv', | ||||||
|  |     'US Election Certification (Olympus).csv', | ||||||
|  |     'US Election Certification (Non-Olympus).csv', | ||||||
|  |     'IN Election Certification (Olympus).csv', | ||||||
|  |     'IN Election Certification (Non-Olympus).csv', | ||||||
|  |     'NY Election Certification (Olympus).csv', | ||||||
|  |     'NY Election Certification (Non-Olympus).csv', | ||||||
|  |     'Ticket Seller Certification (Olympus).csv', | ||||||
|  |     'Ticket Seller Certification (Non-Olympus).csv', | ||||||
|  |     'Pharma Certification EMEA (Olympus).csv', | ||||||
|  |     'Pharma Certification EMEA (Non-Olympus).csv', | ||||||
|  |     'CSFP Certification (Olympus).csv', | ||||||
|  |     'CSFP Certification (Non-Olympus).csv', | ||||||
|  |     'Social Casino Games Certification (Olympus).csv', | ||||||
|  |     'Social Casino Games Certification (NonOlympus).csv', | ||||||
|  |     'Gambling Certification (Olympus).csv', | ||||||
|  |     'Gambling Certification (Non-Olympus).csv', | ||||||
|  |     'Addiction Services Certification (Olympus).csv', | ||||||
|  |     'Addiction Services Certification (Non-Olympus).csv', | ||||||
|  |     'HTML5 Application (Olympus).csv', | ||||||
|  |     'HTML5 Application (Non-Olympus).csv', | ||||||
|  |     # TODO: Support this once Jason unblocks. | ||||||
|  |     # 'Account Take Over (Olympus).csv', | ||||||
|  |     # TODO: Support this once Jason unblocks. | ||||||
|  |     # 'Account Take Over (Non-Olympus).csv', | ||||||
|  |     'Free Desktop Software Policy (Olympus).csv', | ||||||
|  |     'Free Desktop Software Policy (Non-Olympus).csv', | ||||||
|  |     'Untrustworthy Promotions (Olympus).csv', | ||||||
|  |     'Untrustworthy Promotions (Non-Olympus).csv', | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | # TODO: Dump these CSVs into SQL to run basic queries on the " or " count, etc. | ||||||
|  | # Only 'Hard-Code' fields seem to have " or "s in them; SQL can help me verify | ||||||
|  | # this claim. | ||||||
|  | 
 | ||||||
|  | for x in csvs: | ||||||
|  |     print('# File: "{}"'.format(f.strip_extension(x))) | ||||||
|  |     print(csv_to_proto(f.join(csv_directory, x))) | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | if enable_tests: | ||||||
|  |     tests = [ | ||||||
|  |         # 'EU Election Certification (Olympus).csv', | ||||||
|  |         'EU Election Certification (Non-Olympus).csv', | ||||||
|  |         # 'Non-Sensitive Ads Review (Olympus).csv', | ||||||
|  |     ] | ||||||
|  |     for csv_file in tests: | ||||||
|  |         textproto_file = f.join('expected', | ||||||
|  |                                 f.change_extension('.textproto', csv_file)) | ||||||
|  |         actual = csv_to_proto( | ||||||
|  |             f.ensure_absolute(f.join(csv_directory, csv_file))) | ||||||
|  |         expected = open(textproto_file, 'r').read() | ||||||
|  |         simple_assert(actual, expected, name='csv_to_proto') | ||||||
							
								
								
									
										1014
									
								
								universe/ac_types/attributes.pb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1014
									
								
								universe/ac_types/attributes.pb
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										10
									
								
								universe/ac_types/dict.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								universe/ac_types/dict.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | # Naming options for this module: | ||||||
|  | # - map (like Elixir and standard.el): Overwrites python's built-in `map` function. | ||||||
|  | # - dict: Overwrites python's built-in `dict` function. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def take(ks, xs): | ||||||
|  |     result = {} | ||||||
|  |     for k in ks: | ||||||
|  |         result[k] = xs[k] | ||||||
|  |     return result | ||||||
							
								
								
									
										523
									
								
								universe/ac_types/example.textproto
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										523
									
								
								universe/ac_types/example.textproto
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,523 @@ | ||||||
|  | # Please, see the link below for the definition of the messages in this file. | ||||||
|  | # https://cs.corp.google.com/piper///depot/google3/google/internal/alkali/applications/casesconsultservice/v1/consult.proto | ||||||
|  | 
 | ||||||
|  | # Instances of ConsultMetadata message. | ||||||
|  | 
 | ||||||
|  | ## The following ConsultMetadata instance is just an example. | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9126664"  # Ads Taxonomy: http://composer/9126664 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "gcc" | ||||||
|  |   max_wait_time_for_consult_secs: 3600  # 1 hr | ||||||
|  |   reroute_on_customer_interaction: false | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     # Instances of Attribute message. | ||||||
|  |     id: "^^11"  # Attribute name: PRODUCT. | ||||||
|  |     value: "ac-demo-value1" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^^1"  # Attribute name: CATEGORY. | ||||||
|  |     value: "ac-demo-value2" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     # Instances of Attribute message. | ||||||
|  |     id: "^123"  # Attribute name: XYZ. | ||||||
|  |     value: "sample 1" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     # Instances of Attribute message. | ||||||
|  |     id: "^987"  # Attribute name: Demo. | ||||||
|  |     value: "sample 2" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     # Instance of ConsultInput | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "^0123" | ||||||
|  |     is_optional: true | ||||||
|  |     constant_value: "This is an input" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "^9876" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Note the following test case must be modeled with a LACK of configuration | ||||||
|  | #  * AC-004: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=5:5 | ||||||
|  | # As such, it's imperative that all "autoconsult_test" fixtures also match a language | ||||||
|  | # This test will be performed by specifying a language that's not covered in this configuration | ||||||
|  | 
 | ||||||
|  | # General Success Case - consults to T&S robot test pool | ||||||
|  | # Supports Test Cases: | ||||||
|  | #  * AC-001: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=2:2 | ||||||
|  | #  * AC-003: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=4:4 | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441"  # T&S Taxonomy: http://composer/9249441 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   max_wait_time_for_consult_secs: 3600  # 1 hr | ||||||
|  |   reroute_on_customer_interaction: false | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149"  # Attribute name: neoenum.consult_type https://cases.corp.google.com/Client.html#A~%5E88149 | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |     value: "af"  # Afrikaans | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL"  # FRD Name: Product Group | ||||||
|  |     value: "engineering" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: true | ||||||
|  |     parent_frd_id: "^5305"  # Form.summary_of_issue https://cases.corp.google.com/Client.html#A~%5E5305 | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "FEATURE" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "regular_review" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "BUSINESS_LANGUAGE" | ||||||
|  |     is_optional: true | ||||||
|  |     parent_frd_id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "COMPLEXITY" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # General success case - consults to TDA manual test pool | ||||||
|  | # Defines a non-optional input that will be Empty | ||||||
|  | # Supports Test Cases: | ||||||
|  | #  * AC-002: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=3:3 | ||||||
|  | #  * AC-008: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=9:9 | ||||||
|  | #  * AC-015: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=16:16 | ||||||
|  | #  * AC-016: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=17:17 | ||||||
|  | #  * AC-017: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=18:18 | ||||||
|  | #  * AC-018: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=19:19 | ||||||
|  | #  * AC-019: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=20:20 | ||||||
|  | #  * AC-020: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=21:21 | ||||||
|  | #  * AC-021: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=22:22 | ||||||
|  | #  * AC-022: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=23:23 | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9126664"  # Ads Taxonomy: http://composer/9126664 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "gcc" | ||||||
|  |   max_wait_time_for_consult_secs: 3600  # 1 hr | ||||||
|  |   reroute_on_customer_interaction: false | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149"  # Attribute name: neoenum.consult_type https://cases.corp.google.com/Client.html#A~%5E88149 | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |     value: "da"  # dansk | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "CONSULT_TYPE"  # FRD Name: Consult Type | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: true | ||||||
|  |     parent_frd_id: "^5305"  # Form.summary_of_issue https://cases.corp.google.com/Client.html#A~%5E5305 | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # This tests a misconfigured signal type | ||||||
|  | # Supports Test Cases: | ||||||
|  | #  * AC-005: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=6:6 | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9126664"  # Ads Taxonomy: http://composer/9126664 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "gcc" | ||||||
|  |   max_wait_time_for_consult_secs: 3600  # 1 hr | ||||||
|  |   reroute_on_customer_interaction: false | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149"  # Attribute name: neoenum.consult_type https://cases.corp.google.com/Client.html#A~%5E88149 | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |     value: "en-AU"  # English (Australia) | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "CONSULT_TYPE"  # FRD Name: Consult Type | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: UNKNOWN | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: true | ||||||
|  |     parent_frd_id: "^5305"  # Form.summary_of_issue https://cases.corp.google.com/Client.html#A~%5E5305 | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Defines a non-optional input that will be missing | ||||||
|  | # Supports Test Cases: | ||||||
|  | #  * AC-006: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=7:7 | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9126664"  # Ads Taxonomy: http://composer/9126664 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "gcc" | ||||||
|  |   max_wait_time_for_consult_secs: 3600  # 1 hr | ||||||
|  |   reroute_on_customer_interaction: false | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149"  # Attribute name: neoenum.consult_type https://cases.corp.google.com/Client.html#A~%5E88149 | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |     value: "en-CA"  # English (Canada) | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "CONSULT_TYPE"  # FRD Name: Consult Type | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^999999"  # Made-up attribute ID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Defines a non-optional input that will be empty | ||||||
|  | # Supports Test Cases: | ||||||
|  | #  * AC-008: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=9:9 | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9126664"  # Ads Taxonomy: http://composer/9126664 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "gcc" | ||||||
|  |   max_wait_time_for_consult_secs: 3600  # 1 hr | ||||||
|  |   reroute_on_customer_interaction: false | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149"  # Attribute name: neoenum.consult_type https://cases.corp.google.com/Client.html#A~%5E88149 | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |     value: "en-GB"  # English (United Kingdom) | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "CONSULT_TYPE"  # FRD Name: Consult Type | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: false  # Not optional | ||||||
|  |     parent_frd_id: "^5305"  # Form.summary_of_issue https://cases.corp.google.com/Client.html#A~%5E5305 | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Defines a non-optional input that will be missing | ||||||
|  | # Supports Test Cases: | ||||||
|  | #  * AC-009: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=10:10 | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9126664"  # Ads Taxonomy: http://composer/9126664 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "gcc" | ||||||
|  |   max_wait_time_for_consult_secs: 3600  # 1 hr | ||||||
|  |   reroute_on_customer_interaction: false | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149"  # Attribute name: neoenum.consult_type https://cases.corp.google.com/Client.html#A~%5E88149 | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |     value: "en"  # English | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "CONSULT_TYPE"  # FRD Name: Consult Type | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: false  # Not optional | ||||||
|  |     parent_frd_id: "^999999"  # Made-up attribute ID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Defines multiple non-optional inputs that will not validate | ||||||
|  | # Supports Test Cases: | ||||||
|  | #  * AC-011: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=12:12 | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9126664"  # Ads Taxonomy: http://composer/9126664 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "gcc" | ||||||
|  |   max_wait_time_for_consult_secs: 3600  # 1 hr | ||||||
|  |   reroute_on_customer_interaction: false | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149"  # Attribute name: neoenum.consult_type https://cases.corp.google.com/Client.html#A~%5E88149 | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |     value: "en-IN"  # English (India) | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "CONSULT_TYPE"  # FRD Name: Consult Type | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: false  # Not optional | ||||||
|  |     parent_frd_id: "^999999"  # Made-up attribute ID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: false  # Not optional | ||||||
|  |     parent_frd_id: "^5305"  # Form.summary_of_issue https://cases.corp.google.com/Client.html#A~%5E5305 | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Long Timeout Fixture | ||||||
|  | # Re-Route On Customer Interaction set at this level | ||||||
|  | # Supports Test Cases: | ||||||
|  | #  * AC-013: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=14:14 | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9126664"  # Ads Taxonomy: http://composer/9126664 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "gcc" | ||||||
|  |   max_wait_time_for_consult_secs: 36000  # 10 hrs | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149"  # Attribute name: neoenum.consult_type https://cases.corp.google.com/Client.html#A~%5E88149 | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |     value: "en-NZ"  # English (New Zealand) | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "CONSULT_TYPE"  # FRD Name: Consult Type | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: true | ||||||
|  |     parent_frd_id: "^5305"  # Form.summary_of_issue https://cases.corp.google.com/Client.html#A~%5E5305 | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Zero Timeout Fixture | ||||||
|  | # Supports Test Cases: | ||||||
|  | #  * AC-014: https://docs.google.com/spreadsheets/d/1yec_SVbkGL3vHegNkqFweF-Ea8YaFczeZYuhXChavJM/edit#gid=505426631&range=15:15 | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9126664"  # Ads Taxonomy: http://composer/9126664 | ||||||
|  |   view_id: "Routing" | ||||||
|  |   neo_organization: "gcc" | ||||||
|  |   max_wait_time_for_consult_secs: 0  # 0 hrs | ||||||
|  |   reroute_on_customer_interaction: false | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149"  # Attribute name: neoenum.consult_type https://cases.corp.google.com/Client.html#A~%5E88149 | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^3906"  # Form.ad_language https://cases.corp.google.com/Client.html#A~%5E3906 | ||||||
|  |     value: "en-NZ"  # English (New Zealand) | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "CONSULT_TYPE"  # FRD Name: Consult Type | ||||||
|  |     value: "autoconsult_test" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE"  # FRD Name: Robot Workflow State. | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: true | ||||||
|  |     parent_frd_id: "^5305"  # Form.summary_of_issue https://cases.corp.google.com/Client.html#A~%5E5305 | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,255 @@ | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     value: "Germany" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     value: "Netherlands" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     value: "Spain" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     value: "Check Republic" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     value: "United Kingdom" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,550 @@ | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "Germany" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "Netherlands" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "Spain" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "Check Republic" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "United Kingdom" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus_plus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "Germany" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus_plus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "Netherlands" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus_plus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "Spain" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus_plus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "Check Republic" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_certs" | ||||||
|  |   max_wait_time_for_consult_secs: 86400 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ads_certs_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus_plus" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^16296" | ||||||
|  |     value: "United Kingdom" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "Certifications" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "neo.country" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^16296" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.policy" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "elections" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "neo.complexity" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "neo.product" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,126 @@ | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_Adsreview" | ||||||
|  |   max_wait_time_for_consult_secs: 43200 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ad_review_request_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "ads_review" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^2941" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "FEATURE" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "regular_review" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "BUSINESS_LANGUAGE" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^3906" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "INDENTIFIER_COMPLEXITY" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | consult_settings { | ||||||
|  |   taxonomy_id: "9249441" | ||||||
|  |   view_id: "Routing - Ads_Adsreview" | ||||||
|  |   max_wait_time_for_consult_secs: 43200 | ||||||
|  |   reroute_on_customer_interaction: true | ||||||
|  |   neo_organization: "trust_safety" | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^88149" | ||||||
|  |     value: "ad_review_request_autoconsult" | ||||||
|  |   } | ||||||
|  |   parent_lookup_frds { | ||||||
|  |     id: "^87379" | ||||||
|  |     value: "olympus_plus" | ||||||
|  |   } | ||||||
|  |   parent_return_routing_frds { | ||||||
|  |     id: "ROBOT_WORKFLOW_STATE" | ||||||
|  |     value: "automation_escalate" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "VERTICAL" | ||||||
|  |     value: "ads" | ||||||
|  |   } | ||||||
|  |   consult_routing_frds { | ||||||
|  |     id: "REQUEST_TYPE" | ||||||
|  |     value: "ads_review" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: SIGNAL | ||||||
|  |     consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" | ||||||
|  |     is_optional: false | ||||||
|  |     signal_type: CID | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "ISSUE_DESCRIPTION" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^2941" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "REQUEST" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "unreviewed_ads_keywords_and_extensions" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "FEATURE" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "regular_review" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: PARENT_FRD | ||||||
|  |     consult_frd_id: "BUSINESS_LANGUAGE" | ||||||
|  |     is_optional: false | ||||||
|  |     parent_frd_id: "^3906" | ||||||
|  |   } | ||||||
|  |   inputs { | ||||||
|  |     type: CONSTANT | ||||||
|  |     consult_frd_id: "INDENTIFIER_COMPLEXITY" | ||||||
|  |     is_optional: false | ||||||
|  |     constant_value: "easy" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								universe/ac_types/f.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								universe/ac_types/f.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | ||||||
|  | import os | ||||||
|  | import string | ||||||
|  | from test_utils import simple_assert | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def join(*args): | ||||||
|  |     return os.path.join(*args) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | simple_assert(join("/", "tmp", "a.txt"), "/tmp/a.txt", name="join") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def ensure_absolute(path): | ||||||
|  |     """Ensures `path` is an absolute path.""" | ||||||
|  |     return os.path.abspath(os.path.expanduser(path)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | simple_assert(ensure_absolute("~/a.txt"), | ||||||
|  |               "/usr/local/google/home/wpcarro/a.txt", | ||||||
|  |               name="ensure_absolute") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def filename(path): | ||||||
|  |     """Return just the filename of `path`.""" | ||||||
|  |     return os.path.basename(path) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | simple_assert(filename("~/a.txt"), "a.txt", name="filename") | ||||||
|  | simple_assert(filename("/path/to/file"), "file", name="filename") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def strip_extension(path): | ||||||
|  |     """Remove file extension from path.""" | ||||||
|  |     return os.path.splitext(path)[0] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | simple_assert(strip_extension("~/a.txt"), "~/a", name="filename") | ||||||
|  | simple_assert(strip_extension("/path/to/file.txt"), | ||||||
|  |               "/path/to/file", | ||||||
|  |               name="strip_extension") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def change_extension(ext, path): | ||||||
|  |     """Change `path`'s file extension to `ext`.""" | ||||||
|  |     assert string.starts_with('.', ext) | ||||||
|  |     return strip_extension(path) + ext | ||||||
							
								
								
									
										9
									
								
								universe/ac_types/fs.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								universe/ac_types/fs.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | from glob import glob | ||||||
|  | import f | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def ls(pattern): | ||||||
|  |     """Return a list of files that match `pattern`. This is a DWIM function and | ||||||
|  |     will handle relative paths, absolute paths, etc. It should behave | ||||||
|  |     approximately similarly to GNU's ls.""" | ||||||
|  |     return glob(f.ensure_absolute(pattern)) | ||||||
							
								
								
									
										78
									
								
								universe/ac_types/input_constant.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								universe/ac_types/input_constant.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | from test_utils import simple_assert | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def to_rule_id(x): | ||||||
|  |     """Transform `x` to the casing expected in a rule ID.""" | ||||||
|  |     return x.lower().replace(' ', | ||||||
|  |                              '_').replace('(', | ||||||
|  |                                           '').replace(')', | ||||||
|  |                                                       '').replace('/', '_') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | expected = { | ||||||
|  |     'Cloaking or Cloaking Suspect': 'cloaking_or_cloaking_suspect', | ||||||
|  |     'Phishing': 'phishing', | ||||||
|  |     'System Suspended Advertiser': 'system_suspended_advertiser', | ||||||
|  |     'Affiliate Spam': 'affiliate_spam', | ||||||
|  |     'Untrustworthy Behavior': 'untrustworthy_behavior', | ||||||
|  |     'Payment Fraud': 'payment_fraud', | ||||||
|  |     'Bad Debt': 'bad_debt', | ||||||
|  |     'Sleeper': 'sleeper', | ||||||
|  |     'Gaming': 'gaming', | ||||||
|  |     'Counterfeit': 'counterfeit', | ||||||
|  |     'Coupon Abuse': 'coupon_abuse', | ||||||
|  |     'Fraud chargeback': 'fraud_chargeback', | ||||||
|  |     'Friendly chargeback': 'friendly_chargeback', | ||||||
|  |     'Customer service chargeback': 'customer_service_chargeback', | ||||||
|  |     'Delinquency (wont pay)': 'delinquency_wont_pay', | ||||||
|  |     'Direct Debit Abuse/Suspect': 'direct_debit_abuse_suspect', | ||||||
|  |     'Delinquent Account Abuse': 'delinquent_account_abuse', | ||||||
|  |     'Billing Customer Shutdown': 'billing_customer_shutdown', | ||||||
|  |     'Violations Across Multiple Accounts(VAMA)': | ||||||
|  |     'violations_across_multiple_accountsvama', | ||||||
|  |     'Risk Mitigation Daily Spend Limit Raise': | ||||||
|  |     'risk_mitigation_daily_spend_limit_raise', | ||||||
|  |     'Review Account Under Review': 'review_account_under_review', | ||||||
|  |     'Runaway Spenders Daily Spend Limit': 'runaway_spenders_daily_spend_limit', | ||||||
|  |     'Double Serving': 'double_serving', | ||||||
|  |     'Gaining an Unfair Advantage': 'gaining_an_unfair_advantage', | ||||||
|  |     'Office of Foreign Assets Control (OFAC) Sanctions': | ||||||
|  |     'office_of_foreign_assets_control_ofac_sanctions', | ||||||
|  |     'Terms of service violation': 'terms_of_service_violation', | ||||||
|  |     'Spam': 'spam', | ||||||
|  |     'Sexually Explicit Content': 'sexually_explicit_content', | ||||||
|  |     'Illegal drugs (Dangerous Products)': 'illegal_drugs_dangerous_products', | ||||||
|  |     'Hate speech': 'hate_speech', | ||||||
|  |     'Harrassment': 'harrassment', | ||||||
|  |     'Malicious Or Unwanted Software': 'malicious_or_unwanted_software', | ||||||
|  |     'Online Gambling': 'online_gambling', | ||||||
|  |     'Social Casino Games': 'social_casino_games', | ||||||
|  |     'Online Pharmacy Certification Required': | ||||||
|  |     'online_pharmacy_certification_required', | ||||||
|  |     'Copyrights': 'copyrights', | ||||||
|  |     'Addiction Services': 'addiction_services', | ||||||
|  |     'Elections': 'elections', | ||||||
|  |     'Unwanted Software': 'unwanted_software', | ||||||
|  |     'Event Ticket Reseller': 'event_ticket_reseller', | ||||||
|  |     'Cryptocurrency': 'cryptocurrency', | ||||||
|  |     'Complex Speculative Financial Policy': | ||||||
|  |     'complex_speculative_financial_policy', | ||||||
|  |     'US Elections Ads (New York Only)': 'us_elections_ads_new_york_only', | ||||||
|  |     'Alcohol Information': 'alcohol_information', | ||||||
|  |     'Inappropriate Content': 'inappropriate_content', | ||||||
|  |     'Adult Content': 'adult_content', | ||||||
|  |     'final reason blogger suspension': 'final_reason_blogger_suspension', | ||||||
|  |     'final reason calendar suspension': 'final_reason_calendar_suspension', | ||||||
|  |     'final reason writely suspension': 'final_reason_writely_suspension', | ||||||
|  |     'final reason groups reloaded suspension': | ||||||
|  |     'final_reason_groups_reloaded_suspension', | ||||||
|  |     'final reason gplus suspension': 'final_reason_gplus_suspension', | ||||||
|  |     'Terrorist Content': 'terrorist_content', | ||||||
|  |     'Underage account': 'underage_account', | ||||||
|  |     'Terrorist Content': 'terrorist_content', | ||||||
|  |     'Elections': 'elections', | ||||||
|  |     'Policy System Suspension': 'policy_system_suspension', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | for x, y in expected.items(): | ||||||
|  |     simple_assert(to_rule_id(x), y, 'to_rule_id') | ||||||
							
								
								
									
										9
									
								
								universe/ac_types/log.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								universe/ac_types/log.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | enable = False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def warn(x): | ||||||
|  |     """Print `x` as a warning.""" | ||||||
|  |     if enable: | ||||||
|  |         print('[Warning]: {}'.format(x)) | ||||||
|  |     else: | ||||||
|  |         return None | ||||||
							
								
								
									
										60
									
								
								universe/ac_types/notes_with_jason.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								universe/ac_types/notes_with_jason.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | # TODO: Handle Cartesian product for each "or" in row["Data Source/Value"] | ||||||
|  | 
 | ||||||
|  | # Notes from Jason: | ||||||
|  | # Type | ||||||
|  | # - Require ID | ||||||
|  | #   - Parent Routing FRD | ||||||
|  | #   - Consult Input | ||||||
|  | # - Raw | ||||||
|  | #   - everything else... | ||||||
|  | # | ||||||
|  | # Hard-Code: CONSTANT...constant_value | ||||||
|  | # | ||||||
|  | # Atlas.CustomerID == SIGNAL | ||||||
|  | # all else (!Hard-Code && !Atlas.CustomerID): Parent FRD | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | consult_settings { | ||||||
|  |   # Consult Parameter | ||||||
|  |   taxonomy_id: # (row['Name'] == 'Taxonomy ID') row['Data Source/Value'] | ||||||
|  |   view_id: # (row['Name'] == 'View ID') row['Data Source/Value'] | ||||||
|  |   neo_organization: # (row['Name'] == 'ORGANIZATION') row['Data Source/Value'] | ||||||
|  |   max_wait_time_for_consult_secs: # (row['Name'] == 'Timeout') hours_to_seconds(as_hours(row['Data Source/Value'])) | ||||||
|  |   reroute_on_customer_interaction: # (row['Name'] == 'Re-Route if Customer Responds') as_yes_no(row['Data Source/Value']) | ||||||
|  | 
 | ||||||
|  |   # Parent Routing FRD | ||||||
|  |   rows | ||||||
|  |   |> filter(row['Type'] == 'Parent Routing FRD') | ||||||
|  |   |> map((row) => parent_lookup_frds { | ||||||
|  |        id: ??? # as_id(row['Name']) | ||||||
|  |        value: ??? row['Data Source/Value'] | ||||||
|  |      }) | ||||||
|  | 
 | ||||||
|  |   # Consult Routing FRD | ||||||
|  |   rows | ||||||
|  |   |> filter(row['Type'] == 'Consult Routing FRD') | ||||||
|  |   |> map((row) => consult_routing_frds { | ||||||
|  |        id: row['Name'] | ||||||
|  |        value: row['Data Source/Value'] | ||||||
|  |      }) | ||||||
|  | 
 | ||||||
|  |   # Return Routing FRD | ||||||
|  |   rows | ||||||
|  |   |> filter(row['Type'] == 'Return Routing FRD') | ||||||
|  |   |> map((row) => parent_return_routing_frds { | ||||||
|  |        id: ??? | ||||||
|  |        value: ??? | ||||||
|  |      }) | ||||||
|  | 
 | ||||||
|  |   # Input (Note: There seem to be more than one variant of these) | ||||||
|  |   rows | ||||||
|  |   |> filter(row['Type'] == 'Input') | ||||||
|  |   |> map((row) => inputs { | ||||||
|  |        constant_value: ??? | ||||||
|  |        consult_frd_id: ??? | ||||||
|  |        is_optional: ??? | ||||||
|  |        parent_frd_id: ??? | ||||||
|  |        signal_type: ??? | ||||||
|  |        type: ??? | ||||||
|  |      }) | ||||||
|  | } | ||||||
							
								
								
									
										4483
									
								
								universe/ac_types/output.textproto
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4483
									
								
								universe/ac_types/output.textproto
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										138
									
								
								universe/ac_types/parse.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								universe/ac_types/parse.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,138 @@ | ||||||
|  | import re | ||||||
|  | import string | ||||||
|  | from test_utils import simple_assert | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def if_empty(x, parser): | ||||||
|  |     """If the field is empty, use `x`, otherwise, call `parser` on it.""" | ||||||
|  |     def fn(y): | ||||||
|  |         if y == "": | ||||||
|  |             return x | ||||||
|  |         else: | ||||||
|  |             return parser(y) | ||||||
|  | 
 | ||||||
|  |     return fn | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # nullable :: Parser -> Parser | ||||||
|  | def nullable(parser): | ||||||
|  |     def fn(x): | ||||||
|  |         if x == "": | ||||||
|  |             return None | ||||||
|  |         else: | ||||||
|  |             return parser(x) | ||||||
|  | 
 | ||||||
|  |     return fn | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def required(column_name, parser): | ||||||
|  |     def fn(x): | ||||||
|  |         if x == "": | ||||||
|  |             raise Exception( | ||||||
|  |                 "\"{}\" is a required field and cannot be empty".format( | ||||||
|  |                     column_name)) | ||||||
|  |         else: | ||||||
|  |             return parser(x) | ||||||
|  | 
 | ||||||
|  |     return fn | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def apply_parser(parser, row): | ||||||
|  |     """Calls each value in `parser` on the corresponding field in the | ||||||
|  |     dictionary, `row`.""" | ||||||
|  |     result = {} | ||||||
|  |     for k, fn in parser.items(): | ||||||
|  |         result[k] = fn(row[k]) | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def raise_parse_error(x, expected): | ||||||
|  |     """Raises a generic `Exception` when `x` is not a member of the `expected` | ||||||
|  |     set.""" | ||||||
|  |     raise Exception("\"{}\" is none of the following: \"{}\"".format( | ||||||
|  |         x, ", ".join(expected))) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def as_hours(x): | ||||||
|  |     match = re.search(r'(\d+) hours', x) | ||||||
|  |     if match: | ||||||
|  |         try: | ||||||
|  |             return int(match[1]) | ||||||
|  |         except: | ||||||
|  |             raise Exception('Failed to parse {} as an int'.format(match[1])) | ||||||
|  |     else: | ||||||
|  |         raise Exception('Failed to parse {} as hours'.format(x)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = as_hours('24 hours') | ||||||
|  | expected = 24 | ||||||
|  | simple_assert(actual, expected, name='as_hours') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def as_mapping(mapping): | ||||||
|  |     def fn(x): | ||||||
|  |         if mapping[x]: | ||||||
|  |             return mapping[x] | ||||||
|  |         else: | ||||||
|  |             raise_parse_error(x, set(mapping.keys())) | ||||||
|  | 
 | ||||||
|  |     return fn | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # as_yes_no :: String -> Boolean | ||||||
|  | def as_yes_no(x): | ||||||
|  |     """Attempt to parse `x`, a Yes or No value, into a boolean.""" | ||||||
|  |     if x == "Yes": | ||||||
|  |         return True | ||||||
|  |     elif x == "No": | ||||||
|  |         return False | ||||||
|  |     else: | ||||||
|  |         raise_parse_error(x, {"Yes", "No"}) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # as_data_source_type :: String -> String | ||||||
|  | def as_data_source_type(x): | ||||||
|  |     """Attempt to parse `x` as the Data Source Type column, which is an enum | ||||||
|  |     defined in the go/consult-types-authwf-auto-consult sheet.""" | ||||||
|  |     acceptable = {"Hard-Code", "N/A", "Atlas", "Form"} | ||||||
|  |     if x not in acceptable: | ||||||
|  |         raise_parse_error(x, acceptable) | ||||||
|  |     return x | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # as_type :: String -> String | ||||||
|  | def as_type(x): | ||||||
|  |     """Attempt to parse `x` as the Type column, which is an enum defined in the | ||||||
|  |     go/consult-types-authwf-auto-consult sheet.""" | ||||||
|  |     acceptable = { | ||||||
|  |         "Parent Routing FRD", "Consult Parameter", "Consult Routing FRD", | ||||||
|  |         "Input", "Return Routing FRD" | ||||||
|  |     } | ||||||
|  |     if x not in acceptable: | ||||||
|  |         raise_parse_error(x, acceptable) | ||||||
|  |     return x | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def as_int(x): | ||||||
|  |     return int(x) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def as_union_type(x): | ||||||
|  |     if " or " in x: | ||||||
|  |         return [string.trim_surrounding('"', x) for x in x.split(" or ")] | ||||||
|  |     else: | ||||||
|  |         return [x] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | simple_assert(as_union_type('Non-Union'), ['Non-Union'], name='as_union_type') | ||||||
|  | 
 | ||||||
|  | simple_assert(as_union_type( | ||||||
|  |     '"Germany" or "Netherlands" or "Spain" or "Check Republic" or "United Kingdom"' | ||||||
|  | ), ['Germany', 'Netherlands', 'Spain', 'Check Republic', 'United Kingdom'], | ||||||
|  |               name='as_union_type') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # identity :: a -> a | ||||||
|  | def identity(x): | ||||||
|  |     """Returns `x` unchanged.""" | ||||||
|  |     return x | ||||||
							
								
								
									
										
											BIN
										
									
								
								universe/ac_types/parse.pyc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								universe/ac_types/parse.pyc
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										19
									
								
								universe/ac_types/prelude.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								universe/ac_types/prelude.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | ||||||
|  | from test_utils import simple_assert | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def pipe(x, fns): | ||||||
|  |     """Apply `x` to the first function in `fns` and then use its output as the | ||||||
|  |     input for the next function in the list.""" | ||||||
|  |     result = x | ||||||
|  |     for f in fns: | ||||||
|  |         result = f(result) | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = pipe(10, [ | ||||||
|  |     lambda x: x + 3, | ||||||
|  |     lambda x: x - 1, | ||||||
|  |     lambda x: x * 2, | ||||||
|  | ]) | ||||||
|  | expected = (((10 + 3) - 1) * 2) | ||||||
|  | simple_assert(actual, expected, name="pipe") | ||||||
							
								
								
									
										7
									
								
								universe/ac_types/pretty.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								universe/ac_types/pretty.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | from pprint import PrettyPrinter | ||||||
|  | 
 | ||||||
|  | printer = PrettyPrinter(indent=2) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def pretty_print(x): | ||||||
|  |     return printer.pprint(x) | ||||||
							
								
								
									
										
											BIN
										
									
								
								universe/ac_types/pretty.pyc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								universe/ac_types/pretty.pyc
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										18
									
								
								universe/ac_types/regex.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								universe/ac_types/regex.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | import re | ||||||
|  | from test_utils import simple_assert | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def remove(regex, x): | ||||||
|  |     """Attempt to remove the substring matching `re` from `x`.""" | ||||||
|  |     return re.sub(regex, '', x) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # No occurence | ||||||
|  | simple_assert(remove(r'\s\([\w\s]+\)$', 'Atlas.CustomerId'), | ||||||
|  |               'Atlas.CustomerId', | ||||||
|  |               name="remove") | ||||||
|  | # Single occurence | ||||||
|  | simple_assert(remove(r'\s\([\w\s]+\)$', | ||||||
|  |                      'Atlas.CustomerId (adjusted for MCC IDs)'), | ||||||
|  |               'Atlas.CustomerId', | ||||||
|  |               name="remove") | ||||||
							
								
								
									
										51
									
								
								universe/ac_types/scrape.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								universe/ac_types/scrape.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | ||||||
|  | from test_utils import simple_assert | ||||||
|  | import re | ||||||
|  | import log | ||||||
|  | import regex | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Warning: This is a linear search each time. Quite scrappy and hackish, but it | ||||||
|  | # works. Trademarked... | ||||||
|  | def attribute_id_for(x, error_when_absent=True): | ||||||
|  |     """Return the Attribute ID for `x` as defined in attributes.pb.""" | ||||||
|  |     is_next = False | ||||||
|  |     known = { | ||||||
|  |         'Form.description': '^2941', | ||||||
|  |         'Form.country': '^16296', | ||||||
|  |         # Entering this here since some of the CSV have malformed data, and I'm | ||||||
|  |         # not currently interested in a more robust solution. | ||||||
|  |         'form.country': '^16296', | ||||||
|  |         'Form.countries_business_serve': '^14659', | ||||||
|  |         'Form.name': '^1665', | ||||||
|  |     } | ||||||
|  |     if x in known: | ||||||
|  |         return known[x] | ||||||
|  | 
 | ||||||
|  |     for line in open('attributes.pb', 'r').readlines(): | ||||||
|  |         if is_next: | ||||||
|  |             return line[7:-2] | ||||||
|  |         if x in line: | ||||||
|  |             is_next = True | ||||||
|  |         else: | ||||||
|  |             is_next = False | ||||||
|  |     if error_when_absent: | ||||||
|  |         raise Exception("Could not find \"{}\" in the protobuf.".format(x)) | ||||||
|  |     else: | ||||||
|  |         return '{} # TODO(wpcarro): scrape.attribute_id_for could not find the ID.'.format( | ||||||
|  |             x) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = [ | ||||||
|  |     attribute_id_for('Form.description'), | ||||||
|  |     attribute_id_for('Form.country'), | ||||||
|  |     attribute_id_for('Form.countries_business_serve'), | ||||||
|  |     attribute_id_for('Form.name'), | ||||||
|  |     attribute_id_for('Atlas.CustomerId'), | ||||||
|  |     attribute_id_for('Form.form-id'), | ||||||
|  |     attribute_id_for('Form.Ar_descr_textbox'), | ||||||
|  |     attribute_id_for('AR.ART.LastDecisionRecommended'), | ||||||
|  | ] | ||||||
|  | expected = [ | ||||||
|  |     '^2941', '^16296', '^14659', '^1665', '^1421', '^4297', '^6664', '^106918' | ||||||
|  | ] | ||||||
|  | simple_assert(actual, expected) | ||||||
							
								
								
									
										67
									
								
								universe/ac_types/serialize.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								universe/ac_types/serialize.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | ||||||
|  | from test_utils import simple_assert | ||||||
|  | import string as string | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def literal(x): | ||||||
|  |     if x == True: | ||||||
|  |         return 'true' | ||||||
|  |     elif x == False: | ||||||
|  |         return 'false' | ||||||
|  |     elif isinstance(x, int): | ||||||
|  |         return str(x) | ||||||
|  |     elif x is None: | ||||||
|  |         raise Exception("None!") | ||||||
|  |     # `x` is a string | ||||||
|  |     else: | ||||||
|  |         x = string.trim_surrounding('"', x) | ||||||
|  |         return "\"{}\"".format(x) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = [ | ||||||
|  |     literal(True), | ||||||
|  |     literal(9249441), | ||||||
|  |     literal("COMPLEXITY"), | ||||||
|  |     literal("\"doubly wrapped string\"") | ||||||
|  | ] | ||||||
|  | expected = ["true", "9249441", "\"COMPLEXITY\"", "\"doubly wrapped string\""] | ||||||
|  | simple_assert(actual, expected, name="literal") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def input(input_type=None, fields=None): | ||||||
|  |     header = 'inputs {' | ||||||
|  |     input_type_field = '  type: {}'.format(input_type) | ||||||
|  |     fields = '\n'.join( | ||||||
|  |         ["  {}: {}".format(k, literal(v)) for k, v in fields.items()]) | ||||||
|  |     if input_type == 'SIGNAL': | ||||||
|  |         fields += '\n  signal_type: CID' | ||||||
|  |     footer = '}' | ||||||
|  |     return '\n'.join([header, input_type_field, fields, footer]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = input(input_type='CONSTANT', | ||||||
|  |                fields={ | ||||||
|  |                    'consult_frd_id': 'FEATURE', | ||||||
|  |                    'is_optional': False, | ||||||
|  |                    'constant_value': "regular_review", | ||||||
|  |                }) | ||||||
|  | expected = """inputs { | ||||||
|  |   type: CONSTANT | ||||||
|  |   consult_frd_id: "FEATURE" | ||||||
|  |   is_optional: false | ||||||
|  |   constant_value: "regular_review" | ||||||
|  | }""" | ||||||
|  | simple_assert(actual, expected, name='input') | ||||||
|  | 
 | ||||||
|  | actual = input(input_type='CONSTANT', | ||||||
|  |                fields={ | ||||||
|  |                    'consult_frd_id': 'FEATURE', | ||||||
|  |                    'is_optional': False, | ||||||
|  |                    'constant_value': "\"doubly wrapped string\"", | ||||||
|  |                }) | ||||||
|  | expected = """inputs { | ||||||
|  |   type: CONSTANT | ||||||
|  |   consult_frd_id: "FEATURE" | ||||||
|  |   is_optional: false | ||||||
|  |   constant_value: "doubly wrapped string" | ||||||
|  | }""" | ||||||
|  | simple_assert(actual, expected, name='input') | ||||||
							
								
								
									
										90
									
								
								universe/ac_types/string.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								universe/ac_types/string.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,90 @@ | ||||||
|  | from test_utils import simple_assert | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def with_banner(x): | ||||||
|  |     header = '#################################################################' | ||||||
|  |     text = '# {}'.format(x) | ||||||
|  |     footer = '#################################################################' | ||||||
|  |     return '\n'.join([header, text, footer]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def starts_with(prefix, x): | ||||||
|  |     """Return True if `x` starts with `prefix`.""" | ||||||
|  |     return x.startswith(prefix) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def ends_with(prefix, x): | ||||||
|  |     """Return True if `x` starts with `prefix`.""" | ||||||
|  |     return x.endswith(prefix) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def trim_prefix(prefix, x): | ||||||
|  |     """Remove `prefix` from `x` if present.""" | ||||||
|  |     if x.startswith(prefix): | ||||||
|  |         return x[len(prefix):] | ||||||
|  |     else: | ||||||
|  |         return x | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = [trim_prefix('"', '"leading"'), trim_prefix('"', 'non-leading')] | ||||||
|  | expected = ['leading"', 'non-leading'] | ||||||
|  | simple_assert(actual, expected, name="trim_prefix") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def trim_suffix(suffix, x): | ||||||
|  |     """Remove `suffix` from `x` if present.""" | ||||||
|  |     if x.endswith(suffix): | ||||||
|  |         amt = len(x) - len(suffix) | ||||||
|  |         return x[0:amt] | ||||||
|  |     else: | ||||||
|  |         return x | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = [ | ||||||
|  |     trim_suffix('ing"', '"trailing"'), | ||||||
|  |     trim_suffix('ing"', 'non-trailing') | ||||||
|  | ] | ||||||
|  | expected = ['"trail', 'non-trailing'] | ||||||
|  | simple_assert(actual, expected, name="trim_suffix") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def trim_surrounding(b, x): | ||||||
|  |     """Remove `b` from `x` if present as prefix and suffix.""" | ||||||
|  |     if x.startswith(b) and x.endswith(b): | ||||||
|  |         x = trim_prefix(b, x) | ||||||
|  |         x = trim_suffix(b, x) | ||||||
|  |         return x | ||||||
|  |     else: | ||||||
|  |         return x | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = [ | ||||||
|  |     trim_surrounding('"', '"surrounded"'), | ||||||
|  |     trim_surrounding('"', 'non-surrounded'), | ||||||
|  |     trim_surrounding('"', '"just-prefixed'), | ||||||
|  |     trim_surrounding('"', 'just-suffixed"'), | ||||||
|  | ] | ||||||
|  | expected = ['surrounded', 'non-surrounded', '"just-prefixed', 'just-suffixed"'] | ||||||
|  | simple_assert(actual, expected, name="trim_surrounding") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def indent(x, spaces=2): | ||||||
|  |     """Indent string `x` number of `spaces`, defaulting to two.""" | ||||||
|  |     return '\n'.join([' ' * spaces + line for line in x.split('\n')]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | actual = indent("""testing | ||||||
|  | this function | ||||||
|  | out""") | ||||||
|  | expected = """  testing | ||||||
|  |   this function | ||||||
|  |   out""" | ||||||
|  | simple_assert(actual, expected, name="indent") | ||||||
|  | 
 | ||||||
|  | actual = indent("""testing | ||||||
|  | this function | ||||||
|  | out""", spaces=4) | ||||||
|  | expected = """    testing | ||||||
|  |     this function | ||||||
|  |     out""" | ||||||
|  | simple_assert(actual, expected, name="indent") | ||||||
							
								
								
									
										
											BIN
										
									
								
								universe/ac_types/string.pyc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								universe/ac_types/string.pyc
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										21
									
								
								universe/ac_types/test_utils.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								universe/ac_types/test_utils.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | silent_on_success = True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # I need to define this herein to avoid introducing a circular dependency. | ||||||
|  | def with_banner(x): | ||||||
|  |     header = '#################################################################' | ||||||
|  |     text = '# {}'.format(x) | ||||||
|  |     footer = '#################################################################' | ||||||
|  |     return '\n'.join([header, text, footer]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def simple_assert(actual, expected, name=None): | ||||||
|  |     try: | ||||||
|  |         assert actual == expected | ||||||
|  |         if silent_on_success: | ||||||
|  |             return None | ||||||
|  |         else: | ||||||
|  |             print(with_banner('{}: Test passes!'.format(name))) | ||||||
|  |     except: | ||||||
|  |         print(with_banner('{}: Test failure.'.format(name))) | ||||||
|  |         print(actual) | ||||||
							
								
								
									
										
											BIN
										
									
								
								universe/ac_types/test_utils.pyc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								universe/ac_types/test_utils.pyc
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										5
									
								
								universe/ac_types/todo.org
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								universe/ac_types/todo.org
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | * DONE Ensure order matches trix. | ||||||
|  | * DONE Use FRD identifier and not its display name for inputs.CONSTANT. | ||||||
|  | * DONE Ensure "Non-Sensitive Ads Review (Olympus)" support "olympus" and "olympus_plus". | ||||||
|  | * DONE Append output to consult.textproto. | ||||||
|  | * DONE Run `hg fix` on consult.textproto. | ||||||
							
								
								
									
										8
									
								
								universe/advent-of-code/aoc2019.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								universe/advent-of-code/aoc2019.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | with import <nixpkgs> {}; | ||||||
|  | with python35Packages; | ||||||
|  | 
 | ||||||
|  | buildPythonPackage { | ||||||
|  |   name = "wpcarro"; | ||||||
|  |   src = ./day_5.py; | ||||||
|  |   propagatedBuildInputs = [ pytest numpy ]; | ||||||
|  | } | ||||||
							
								
								
									
										119
									
								
								universe/advent-of-code/day_1.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								universe/advent-of-code/day_1.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,119 @@ | ||||||
|  | from math import floor | ||||||
|  | 
 | ||||||
|  | xs = [ | ||||||
|  |     102473, | ||||||
|  |     84495, | ||||||
|  |     98490, | ||||||
|  |     68860, | ||||||
|  |     62204, | ||||||
|  |     72810, | ||||||
|  |     65185, | ||||||
|  |     145951, | ||||||
|  |     77892, | ||||||
|  |     108861, | ||||||
|  |     70764, | ||||||
|  |     67286, | ||||||
|  |     74002, | ||||||
|  |     80773, | ||||||
|  |     52442, | ||||||
|  |     131505, | ||||||
|  |     107162, | ||||||
|  |     126993, | ||||||
|  |     59784, | ||||||
|  |     64231, | ||||||
|  |     91564, | ||||||
|  |     68585, | ||||||
|  |     98735, | ||||||
|  |     69020, | ||||||
|  |     77332, | ||||||
|  |     60445, | ||||||
|  |     65826, | ||||||
|  |     111506, | ||||||
|  |     95431, | ||||||
|  |     146687, | ||||||
|  |     135119, | ||||||
|  |     86804, | ||||||
|  |     95915, | ||||||
|  |     85434, | ||||||
|  |     111303, | ||||||
|  |     148127, | ||||||
|  |     132921, | ||||||
|  |     136213, | ||||||
|  |     89004, | ||||||
|  |     143137, | ||||||
|  |     144853, | ||||||
|  |     143017, | ||||||
|  |     104386, | ||||||
|  |     100612, | ||||||
|  |     54760, | ||||||
|  |     63813, | ||||||
|  |     144191, | ||||||
|  |     84481, | ||||||
|  |     69718, | ||||||
|  |     84936, | ||||||
|  |     98621, | ||||||
|  |     124993, | ||||||
|  |     92736, | ||||||
|  |     60369, | ||||||
|  |     137284, | ||||||
|  |     101902, | ||||||
|  |     112726, | ||||||
|  |     51784, | ||||||
|  |     126496, | ||||||
|  |     85005, | ||||||
|  |     101661, | ||||||
|  |     137278, | ||||||
|  |     136637, | ||||||
|  |     90340, | ||||||
|  |     100209, | ||||||
|  |     53683, | ||||||
|  |     50222, | ||||||
|  |     132060, | ||||||
|  |     98797, | ||||||
|  |     139054, | ||||||
|  |     135638, | ||||||
|  |     100632, | ||||||
|  |     137849, | ||||||
|  |     125333, | ||||||
|  |     103981, | ||||||
|  |     76954, | ||||||
|  |     134352, | ||||||
|  |     74229, | ||||||
|  |     93402, | ||||||
|  |     62552, | ||||||
|  |     50286, | ||||||
|  |     57066, | ||||||
|  |     98439, | ||||||
|  |     120708, | ||||||
|  |     117827, | ||||||
|  |     107884, | ||||||
|  |     72837, | ||||||
|  |     148663, | ||||||
|  |     125645, | ||||||
|  |     61460, | ||||||
|  |     120555, | ||||||
|  |     142473, | ||||||
|  |     106668, | ||||||
|  |     58612, | ||||||
|  |     58576, | ||||||
|  |     143366, | ||||||
|  |     90058, | ||||||
|  |     121087, | ||||||
|  |     89546, | ||||||
|  |     126161, | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def fuel_for_mass(x): | ||||||
|  |     """Return the amount of fuel (in mass) required for a mass of X. The total | ||||||
|  |     amount of fuel includes the amount of fuel required for the fuel itself, | ||||||
|  |     since fuel also has a mass weights.""" | ||||||
|  |     mass_fuel = floor(x / 3) - 2 | ||||||
|  |     if mass_fuel < 0: | ||||||
|  |         return 0 | ||||||
|  |     else: | ||||||
|  |         fuel_fuel = fuel_for_mass(mass_fuel) | ||||||
|  |         return mass_fuel + fuel_fuel | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | print(sum(fuel_for_mass(x) for x in xs)) | ||||||
							
								
								
									
										32
									
								
								universe/advent-of-code/day_2.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								universe/advent-of-code/day_2.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | from itertools import product | ||||||
|  | 
 | ||||||
|  | x = [ | ||||||
|  |     1, 0, 0, 3, 1, 1, 2, 3, 1, 3, 4, 3, 1, 5, 0, 3, 2, 1, 10, 19, 1, 6, 19, 23, | ||||||
|  |     2, 23, 6, 27, 2, 6, 27, 31, 2, 13, 31, 35, 1, 10, 35, 39, 2, 39, 13, 43, 1, | ||||||
|  |     43, 13, 47, 1, 6, 47, 51, 1, 10, 51, 55, 2, 55, 6, 59, 1, 5, 59, 63, 2, 9, | ||||||
|  |     63, 67, 1, 6, 67, 71, 2, 9, 71, 75, 1, 6, 75, 79, 2, 79, 13, 83, 1, 83, 10, | ||||||
|  |     87, 1, 13, 87, 91, 1, 91, 10, 95, 2, 9, 95, 99, 1, 5, 99, 103, 2, 10, 103, | ||||||
|  |     107, 1, 107, 2, 111, 1, 111, 5, 0, 99, 2, 14, 0, 0 | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def interpret(i, x): | ||||||
|  |     op, a, b, out = x[i + 0], x[i + 1], x[i + 2], x[i + 3] | ||||||
|  |     if op == 1: | ||||||
|  |         x[out] = x[a] + x[b] | ||||||
|  |         return interpret(i + 4, x) | ||||||
|  |     elif op == 2: | ||||||
|  |         x[out] = x[a] * x[b] | ||||||
|  |         return interpret(i + 4, x) | ||||||
|  |     elif op == 99: | ||||||
|  |         return x | ||||||
|  |     else: | ||||||
|  |         raise Exception('Unsupported opcode: {}. {}, {}'.format(op, a, b)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | for a, b in product(range(100), range(100)): | ||||||
|  |     y = x[:] | ||||||
|  |     y[1] = a | ||||||
|  |     y[2] = b | ||||||
|  |     if interpret(0, y)[0] == 19690720: | ||||||
|  |         print(100 * a + b) | ||||||
							
								
								
									
										137
									
								
								universe/advent-of-code/day_3.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								universe/advent-of-code/day_3.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,137 @@ | ||||||
|  | from math import floor | ||||||
|  | from heapq import heappush, heappop | ||||||
|  | 
 | ||||||
|  | xs = [ | ||||||
|  |     "R1009", "U993", "L383", "D725", "R163", "D312", "R339", "U650", "R558", | ||||||
|  |     "U384", "R329", "D61", "L172", "D555", "R160", "D972", "L550", "D801", | ||||||
|  |     "L965", "U818", "L123", "D530", "R176", "D353", "L25", "U694", "L339", | ||||||
|  |     "U600", "L681", "D37", "R149", "D742", "R762", "U869", "R826", "U300", | ||||||
|  |     "L949", "U978", "L303", "U361", "R136", "D343", "L909", "U551", "R745", | ||||||
|  |     "U913", "L566", "D292", "R820", "U886", "R205", "D431", "L93", "D71", | ||||||
|  |     "R577", "U872", "L705", "U510", "L698", "U963", "R607", "U527", "L669", | ||||||
|  |     "D543", "R690", "U954", "L929", "D218", "R490", "U500", "L589", "D332", | ||||||
|  |     "R949", "D538", "R696", "U659", "L188", "U468", "L939", "U833", "L445", | ||||||
|  |     "D430", "R78", "D303", "R130", "D649", "R849", "D712", "L511", "U745", | ||||||
|  |     "R51", "U973", "R799", "U829", "R605", "D771", "L837", "U204", "L414", | ||||||
|  |     "D427", "R538", "U116", "R540", "D168", "R493", "U900", "L679", "U431", | ||||||
|  |     "L521", "D500", "L428", "U332", "L954", "U717", "L853", "D339", "L88", | ||||||
|  |     "U807", "L607", "D496", "L163", "U468", "L25", "U267", "L759", "D898", | ||||||
|  |     "L591", "U445", "L469", "U531", "R596", "D486", "L728", "D677", "R350", | ||||||
|  |     "D429", "R39", "U568", "R92", "D875", "L835", "D841", "R877", "U178", | ||||||
|  |     "L221", "U88", "R592", "U692", "R455", "U693", "L419", "U90", "R609", | ||||||
|  |     "U672", "L293", "U168", "R175", "D456", "R319", "D570", "R504", "D165", | ||||||
|  |     "L232", "D624", "L604", "D68", "R807", "D59", "R320", "D281", "L371", | ||||||
|  |     "U956", "L788", "D897", "L231", "D829", "R287", "D798", "L443", "U194", | ||||||
|  |     "R513", "D925", "L232", "U225", "L919", "U563", "R448", "D889", "R661", | ||||||
|  |     "U852", "L950", "D558", "L269", "U186", "L625", "U673", "L995", "U732", | ||||||
|  |     "R435", "U849", "L413", "D690", "L158", "D234", "R361", "D458", "L271", | ||||||
|  |     "U90", "L781", "U754", "R256", "U162", "L842", "U927", "L144", "D62", | ||||||
|  |     "R928", "D238", "R473", "U97", "L745", "U303", "L487", "D349", "L520", | ||||||
|  |     "D31", "L825", "U385", "L133", "D948", "L39", "U62", "R801", "D664", | ||||||
|  |     "L333", "U134", "R692", "U385", "L658", "U202", "L279", "D374", "R489", | ||||||
|  |     "D686", "L182", "U222", "R733", "U177", "R94", "D603", "L376", "U901", | ||||||
|  |     "R216", "D851", "L155", "D214", "L460", "U758", "R121", "D746", "L180", | ||||||
|  |     "U175", "L943", "U146", "L166", "D251", "L238", "U168", "L642", "D341", | ||||||
|  |     "R281", "U182", "R539", "D416", "R553", "D67", "L748", "U272", "R257", | ||||||
|  |     "D869", "L340", "U180", "R791", "U138", "L755", "D976", "R731", "U713", | ||||||
|  |     "R602", "D284", "L258", "U176", "R509", "U46", "R935", "U576", "R96", | ||||||
|  |     "U89", "L913", "U703", "R833" | ||||||
|  | ] | ||||||
|  | ys = [ | ||||||
|  |     "L1006", "D998", "R94", "D841", "R911", "D381", "R532", "U836", "L299", | ||||||
|  |     "U237", "R781", "D597", "L399", "D800", "L775", "D405", "L485", "U636", | ||||||
|  |     "R589", "D942", "L878", "D779", "L751", "U711", "L973", "U410", "L151", | ||||||
|  |     "U15", "L685", "U417", "L106", "D648", "L105", "D461", "R448", "D743", | ||||||
|  |     "L589", "D430", "R883", "U37", "R155", "U350", "L421", "U23", "R337", | ||||||
|  |     "U816", "R384", "D671", "R615", "D410", "L910", "U914", "L579", "U385", | ||||||
|  |     "R916", "U13", "R268", "D519", "R289", "U410", "L389", "D885", "L894", | ||||||
|  |     "U734", "L474", "U707", "L72", "U155", "L237", "U760", "L127", "U806", | ||||||
|  |     "L15", "U381", "L557", "D727", "L569", "U320", "L985", "D452", "L8", | ||||||
|  |     "D884", "R356", "U732", "L672", "D458", "L485", "U402", "L238", "D30", | ||||||
|  |     "R644", "U125", "R753", "U183", "L773", "U487", "R849", "U210", "L164", | ||||||
|  |     "D808", "L595", "D668", "L340", "U785", "R313", "D72", "L76", "D263", | ||||||
|  |     "R689", "U604", "R471", "U688", "R462", "D915", "R106", "D335", "R869", | ||||||
|  |     "U499", "R190", "D916", "R468", "D882", "R56", "D858", "L143", "D741", | ||||||
|  |     "L386", "U856", "R50", "U853", "R151", "D114", "L773", "U854", "L290", | ||||||
|  |     "D344", "L23", "U796", "L531", "D932", "R314", "U960", "R643", "D303", | ||||||
|  |     "L661", "D493", "L82", "D491", "L722", "U848", "L686", "U4", "L985", | ||||||
|  |     "D509", "L135", "D452", "R500", "U105", "L326", "D101", "R222", "D944", | ||||||
|  |     "L645", "D362", "L628", "U305", "L965", "U356", "L358", "D137", "R787", | ||||||
|  |     "U728", "R967", "U404", "R18", "D928", "L695", "D965", "R281", "D597", | ||||||
|  |     "L791", "U731", "R746", "U163", "L780", "U41", "L255", "U81", "L530", | ||||||
|  |     "D964", "R921", "D297", "R475", "U663", "L226", "U623", "L984", "U943", | ||||||
|  |     "L143", "U201", "R926", "U572", "R343", "U839", "R764", "U751", "R128", | ||||||
|  |     "U939", "R987", "D108", "R474", "U599", "R412", "D248", "R125", "U797", | ||||||
|  |     "L91", "D761", "L840", "U290", "L281", "U779", "R650", "D797", "R185", | ||||||
|  |     "D320", "L25", "U378", "L696", "U332", "R75", "D620", "L213", "D667", | ||||||
|  |     "R558", "U267", "L846", "U306", "R939", "D220", "R311", "U827", "R345", | ||||||
|  |     "U534", "R56", "D679", "R48", "D845", "R898", "U8", "R862", "D960", "R753", | ||||||
|  |     "U319", "L886", "D795", "R805", "D265", "R876", "U729", "R894", "D368", | ||||||
|  |     "R858", "U744", "R506", "D327", "L903", "U919", "L721", "U507", "L463", | ||||||
|  |     "U753", "R775", "D719", "R315", "U128", "R17", "D376", "R999", "D386", | ||||||
|  |     "L259", "U181", "L162", "U605", "L265", "D430", "R35", "D968", "R207", | ||||||
|  |     "U466", "R796", "D667", "R93", "U749", "L315", "D410", "R312", "U929", | ||||||
|  |     "L923", "U260", "R638" | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def to_coords(xs): | ||||||
|  |     row, col = 0, 0 | ||||||
|  |     coords = [] | ||||||
|  |     for x in xs: | ||||||
|  |         d, amt = x[0], int(x[1:]) | ||||||
|  |         if d == 'U': | ||||||
|  |             for i in range(1, amt + 1): | ||||||
|  |                 coords.append((row + i, col)) | ||||||
|  |             row += amt | ||||||
|  |         elif d == 'D': | ||||||
|  |             for i in range(1, amt + 1): | ||||||
|  |                 coords.append((row - i, col)) | ||||||
|  |             row -= amt | ||||||
|  |         elif d == 'L': | ||||||
|  |             for i in range(1, amt + 1): | ||||||
|  |                 coords.append((row, col - i)) | ||||||
|  |             col -= amt | ||||||
|  |         elif d == 'R': | ||||||
|  |             for i in range(1, amt + 1): | ||||||
|  |                 coords.append((row, col + i)) | ||||||
|  |             col += i | ||||||
|  |     return coords | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def contains(row, col, d): | ||||||
|  |     if row not in d: | ||||||
|  |         return False | ||||||
|  |     return col in d[row] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def intersections(xs, ys): | ||||||
|  |     d = {} | ||||||
|  |     ints = set() | ||||||
|  |     for row, col in to_coords(xs): | ||||||
|  |         if row in d: | ||||||
|  |             d[row].add(col) | ||||||
|  |         else: | ||||||
|  |             d[row] = {col} | ||||||
|  |     for row, col in to_coords(ys): | ||||||
|  |         if contains(row, col, d): | ||||||
|  |             ints.add((row, col)) | ||||||
|  |     return ints | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def trace_to(coord, xs): | ||||||
|  |     count = 0 | ||||||
|  |     for coord_x in to_coords(xs): | ||||||
|  |         count += 1 | ||||||
|  |         if coord_x == coord: | ||||||
|  |             return count | ||||||
|  |     raise Exception("Intersection doesn't exist") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | answer = [] | ||||||
|  | for coord in intersections(xs, ys): | ||||||
|  |     x = trace_to(coord, xs) | ||||||
|  |     y = trace_to(coord, ys) | ||||||
|  |     heappush(answer, x + y) | ||||||
|  | 
 | ||||||
|  | print(heappop(answer)) | ||||||
							
								
								
									
										35
									
								
								universe/advent-of-code/day_4.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								universe/advent-of-code/day_4.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | import re | ||||||
|  | 
 | ||||||
|  | start = 134792 | ||||||
|  | end = 675810 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def satisfies(x): | ||||||
|  |     x = str(x) | ||||||
|  |     result = False | ||||||
|  |     double, not_decreasing = False, False | ||||||
|  | 
 | ||||||
|  |     # double and *only* double exists | ||||||
|  |     for i in range(len(x) - 1): | ||||||
|  |         # double and left-of-a  is BOL or !x | ||||||
|  |         #        and right-of-b is EOL or !x | ||||||
|  |         a, b = x[i], x[i + 1] | ||||||
|  |         bol = i - 1 < 0 | ||||||
|  |         eol = i + 2 >= len(x) | ||||||
|  |         if a == b and (bol or x[i - 1] != a) and (eol or x[i + 2] != a): | ||||||
|  |             double = True | ||||||
|  |             break | ||||||
|  | 
 | ||||||
|  |     # not_decreasing | ||||||
|  |     prev = int(x[0]) | ||||||
|  |     for a in x[1:]: | ||||||
|  |         a = int(a) | ||||||
|  |         if prev > a: | ||||||
|  |             return False | ||||||
|  |         prev = a | ||||||
|  |     not_decreasing = True | ||||||
|  | 
 | ||||||
|  |     return double and not_decreasing | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | print(len([x for x in range(start, end + 1) if satisfies(x)])) | ||||||
							
								
								
									
										50
									
								
								universe/advent-of-code/day_5.ex
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								universe/advent-of-code/day_5.ex
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | ||||||
|  | defmodule Interpretter do | ||||||
|  |   def interpret_param({mode, x}, xs) do | ||||||
|  |     case mode do | ||||||
|  |       :positional -> Enum.at(xs, x) | ||||||
|  |       :immediate  -> x | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   # Perhaps I can model the intepretter after Forth and make it a stack-based | ||||||
|  |   # interpretter with an emphasis on debugability, introspection. | ||||||
|  |   def interpret(i, xs) do | ||||||
|  |     stack = [] | ||||||
|  |     op = Enum.at(xs, i) | ||||||
|  | 
 | ||||||
|  |     # map instructions into an intermediate representation (i.e. IR) where the | ||||||
|  |     # opcodes are mapped into atoms and the arguments are mapped into references | ||||||
|  |     # or literals. | ||||||
|  | 
 | ||||||
|  |     instructions = | ||||||
|  |       %{'01' => :add, | ||||||
|  |         '02' => :multiply, | ||||||
|  |         '03' => :input, | ||||||
|  |         '04' => :output, | ||||||
|  |         '05' => :jump_if_true, | ||||||
|  |         '06' => :jump_if_false, | ||||||
|  |         '07' => :less_than, | ||||||
|  |         '08' => :equal_to, | ||||||
|  |         '99' => :return} | ||||||
|  | 
 | ||||||
|  |     case xs do | ||||||
|  |       [:add, a, b, {:positional, out} | rest] -> | ||||||
|  |         a = interpret_param(a, xs) | ||||||
|  |         b = interpret_param(b, xs) | ||||||
|  |         Interpretter.interpret(i + 3, List.insert_at(xs, out, a + b)) | ||||||
|  | 
 | ||||||
|  |       [:multiply, a, b, {:positional, out} | rest] -> | ||||||
|  |         a = interpret_param(a, xs) | ||||||
|  |         b = interpret_param(b, xs) | ||||||
|  |         Interpretter.interpret(i + 3, List.insert_at(xs, out, a * b)) | ||||||
|  | 
 | ||||||
|  |       [:input, a | rest] -> nil | ||||||
|  |       [:output, a | rest] -> nil | ||||||
|  |       [:jump_if_true, a, b | rest] -> nil | ||||||
|  |       [:jump_if_false, a, b | rest] -> nil | ||||||
|  |       [:less_than, a, b, out | rest] -> nil | ||||||
|  |       [:equal_to, a, b, out | rest] -> nil | ||||||
|  |       [:return | _rest] -> nil | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										170
									
								
								universe/advent-of-code/day_5.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								universe/advent-of-code/day_5.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,170 @@ | ||||||
|  | x = [ | ||||||
|  |     3, 225, 1, 225, 6, 6, 1100, 1, 238, 225, 104, 0, 1102, 31, 68, 225, 1001, | ||||||
|  |     13, 87, 224, 1001, 224, -118, 224, 4, 224, 102, 8, 223, 223, 1001, 224, 7, | ||||||
|  |     224, 1, 223, 224, 223, 1, 174, 110, 224, 1001, 224, -46, 224, 4, 224, 102, | ||||||
|  |     8, 223, 223, 101, 2, 224, 224, 1, 223, 224, 223, 1101, 13, 60, 224, 101, | ||||||
|  |     -73, 224, 224, 4, 224, 102, 8, 223, 223, 101, 6, 224, 224, 1, 224, 223, | ||||||
|  |     223, 1101, 87, 72, 225, 101, 47, 84, 224, 101, -119, 224, 224, 4, 224, | ||||||
|  |     1002, 223, 8, 223, 1001, 224, 6, 224, 1, 223, 224, 223, 1101, 76, 31, 225, | ||||||
|  |     1102, 60, 43, 225, 1102, 45, 31, 225, 1102, 63, 9, 225, 2, 170, 122, 224, | ||||||
|  |     1001, 224, -486, 224, 4, 224, 102, 8, 223, 223, 101, 2, 224, 224, 1, 223, | ||||||
|  |     224, 223, 1102, 29, 17, 224, 101, -493, 224, 224, 4, 224, 102, 8, 223, 223, | ||||||
|  |     101, 1, 224, 224, 1, 223, 224, 223, 1102, 52, 54, 225, 1102, 27, 15, 225, | ||||||
|  |     102, 26, 113, 224, 1001, 224, -1560, 224, 4, 224, 102, 8, 223, 223, 101, 7, | ||||||
|  |     224, 224, 1, 223, 224, 223, 1002, 117, 81, 224, 101, -3645, 224, 224, 4, | ||||||
|  |     224, 1002, 223, 8, 223, 101, 6, 224, 224, 1, 223, 224, 223, 4, 223, 99, 0, | ||||||
|  |     0, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1105, 0, 99999, 1105, 227, 247, | ||||||
|  |     1105, 1, 99999, 1005, 227, 99999, 1005, 0, 256, 1105, 1, 99999, 1106, 227, | ||||||
|  |     99999, 1106, 0, 265, 1105, 1, 99999, 1006, 0, 99999, 1006, 227, 274, 1105, | ||||||
|  |     1, 99999, 1105, 1, 280, 1105, 1, 99999, 1, 225, 225, 225, 1101, 294, 0, 0, | ||||||
|  |     105, 1, 0, 1105, 1, 99999, 1106, 0, 300, 1105, 1, 99999, 1, 225, 225, 225, | ||||||
|  |     1101, 314, 0, 0, 106, 0, 0, 1105, 1, 99999, 8, 226, 677, 224, 102, 2, 223, | ||||||
|  |     223, 1005, 224, 329, 1001, 223, 1, 223, 1108, 677, 226, 224, 102, 2, 223, | ||||||
|  |     223, 1006, 224, 344, 101, 1, 223, 223, 108, 677, 226, 224, 102, 2, 223, | ||||||
|  |     223, 1006, 224, 359, 101, 1, 223, 223, 7, 677, 226, 224, 102, 2, 223, 223, | ||||||
|  |     1005, 224, 374, 101, 1, 223, 223, 1007, 226, 677, 224, 102, 2, 223, 223, | ||||||
|  |     1005, 224, 389, 101, 1, 223, 223, 8, 677, 677, 224, 102, 2, 223, 223, 1006, | ||||||
|  |     224, 404, 1001, 223, 1, 223, 1007, 677, 677, 224, 1002, 223, 2, 223, 1006, | ||||||
|  |     224, 419, 101, 1, 223, 223, 1108, 677, 677, 224, 1002, 223, 2, 223, 1005, | ||||||
|  |     224, 434, 1001, 223, 1, 223, 1107, 226, 677, 224, 102, 2, 223, 223, 1005, | ||||||
|  |     224, 449, 101, 1, 223, 223, 107, 226, 226, 224, 102, 2, 223, 223, 1006, | ||||||
|  |     224, 464, 101, 1, 223, 223, 1108, 226, 677, 224, 1002, 223, 2, 223, 1005, | ||||||
|  |     224, 479, 1001, 223, 1, 223, 7, 677, 677, 224, 102, 2, 223, 223, 1006, 224, | ||||||
|  |     494, 1001, 223, 1, 223, 1107, 677, 226, 224, 102, 2, 223, 223, 1005, 224, | ||||||
|  |     509, 101, 1, 223, 223, 107, 677, 677, 224, 1002, 223, 2, 223, 1006, 224, | ||||||
|  |     524, 101, 1, 223, 223, 1008, 677, 677, 224, 1002, 223, 2, 223, 1006, 224, | ||||||
|  |     539, 101, 1, 223, 223, 7, 226, 677, 224, 1002, 223, 2, 223, 1005, 224, 554, | ||||||
|  |     101, 1, 223, 223, 108, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 569, | ||||||
|  |     101, 1, 223, 223, 1008, 226, 677, 224, 102, 2, 223, 223, 1005, 224, 584, | ||||||
|  |     101, 1, 223, 223, 8, 677, 226, 224, 1002, 223, 2, 223, 1005, 224, 599, 101, | ||||||
|  |     1, 223, 223, 1007, 226, 226, 224, 1002, 223, 2, 223, 1005, 224, 614, 101, | ||||||
|  |     1, 223, 223, 1107, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 629, 101, | ||||||
|  |     1, 223, 223, 107, 677, 226, 224, 1002, 223, 2, 223, 1005, 224, 644, 1001, | ||||||
|  |     223, 1, 223, 1008, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 659, 101, | ||||||
|  |     1, 223, 223, 108, 677, 677, 224, 1002, 223, 2, 223, 1005, 224, 674, 1001, | ||||||
|  |     223, 1, 223, 4, 223, 99, 226 | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | # Interpretter spec: | ||||||
|  | # Op-code width: 2 | ||||||
|  | # ABCDE | ||||||
|  | # A:  Mode of 3rd parameter | ||||||
|  | # B:  Mode of 2rd parameter | ||||||
|  | # C:  Mode of 1st parameter | ||||||
|  | # DE: 2-digit op-code | ||||||
|  | # | ||||||
|  | # Not every op-code has the same arity. | ||||||
|  | # | ||||||
|  | # Parameter modes: | ||||||
|  | # - positional: index of memory. 0 | ||||||
|  | # - immediate: raw value. 1 | ||||||
|  | # Assert that you never attempt to write to an "immediate value" | ||||||
|  | 
 | ||||||
|  | # Parameter modes | ||||||
|  | POS = '0'  # positional parameter mode | ||||||
|  | VAL = '1'  # immediate parameter mode | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Pasted from day-2.py | ||||||
|  | # interpretter :: Int -> [Int] -> [Int] -> IO () | ||||||
|  | def interpret(i, x, argv=[], outs=[]): | ||||||
|  |     """Values in `argv` will be applied to any `input` fields.""" | ||||||
|  |     # The widest op-code we'll see is 3 + 2 = 5 for either addition or | ||||||
|  |     # multiplication since each of those is a 3-arity function with a two-digit | ||||||
|  |     # op-code. | ||||||
|  |     instruction = '{:05d}'.format(x[i]) | ||||||
|  |     op = instruction[-2:] | ||||||
|  | 
 | ||||||
|  |     if op == '01': | ||||||
|  |         a, b, out = x[i + 1], x[i + 2], x[i + 3] | ||||||
|  |         mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[ | ||||||
|  |             0] | ||||||
|  |         a = a if mode_a == VAL else x[a] | ||||||
|  |         b = b if mode_b == VAL else x[b] | ||||||
|  |         assert mode_out == POS | ||||||
|  |         x[out] = a + b | ||||||
|  |         return interpret(i + 4, x, argv=argv, outs=outs) | ||||||
|  |     elif op == '02': | ||||||
|  |         a, b, out = x[i + 1], x[i + 2], x[i + 3] | ||||||
|  |         mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[ | ||||||
|  |             0] | ||||||
|  |         a = a if mode_a == VAL else x[a] | ||||||
|  |         b = b if mode_b == VAL else x[b] | ||||||
|  |         assert mode_out == POS | ||||||
|  |         x[out] = a * b | ||||||
|  |         return interpret(i + 4, x, argv=argv, outs=outs) | ||||||
|  |     # input | ||||||
|  |     elif op == '03': | ||||||
|  |         a = x[i + 1] | ||||||
|  |         mode_a = instruction[2] | ||||||
|  |         assert mode_a == POS | ||||||
|  |         # What's the pythonic way to defensively get this value? | ||||||
|  |         if len(argv) and argv[0] is not None: | ||||||
|  |             x[a] = argv[0] | ||||||
|  |             return interpret(i + 2, x, argv=argv[1:], outs=outs) | ||||||
|  |         elif len(outs) and outs[-1] is not None: | ||||||
|  |             x[a] = outs[-1] | ||||||
|  |             return interpret(i + 2, x, argv=argv, outs=outs) | ||||||
|  |         else: | ||||||
|  |             # Here we want to block until the user applies input. This could be | ||||||
|  |             # done easily with message passing for something similar. | ||||||
|  |             x[a] = int(input('Enter: ')) | ||||||
|  |             return interpret(i + 2, x, argv=argv) | ||||||
|  |     # output | ||||||
|  |     elif op == '04': | ||||||
|  |         a = x[i + 1] | ||||||
|  |         mode_a = instruction[2] | ||||||
|  |         a = a if mode_a == VAL else x[a] | ||||||
|  |         outs.append(a) | ||||||
|  |         return interpret(i + 2, x, argv=argv, outs=outs) | ||||||
|  |     # jump-if-true | ||||||
|  |     elif op == '05': | ||||||
|  |         a, b = x[i + 1], x[i + 2] | ||||||
|  |         mode_a, mode_b = instruction[2], instruction[1] | ||||||
|  |         a = a if mode_a == VAL else x[a] | ||||||
|  |         b = b if mode_b == VAL else x[b] | ||||||
|  |         if a != 0: | ||||||
|  |             return interpret(b, x, argv=argv, outs=outs) | ||||||
|  |         else: | ||||||
|  |             return interpret(i + 3, x, argv=argv, outs=outs) | ||||||
|  |     # jump-if-false | ||||||
|  |     elif op == '06': | ||||||
|  |         a, b = x[i + 1], x[i + 2] | ||||||
|  |         mode_a, mode_b = instruction[2], instruction[1] | ||||||
|  |         a = a if mode_a == VAL else x[a] | ||||||
|  |         b = b if mode_b == VAL else x[b] | ||||||
|  |         if a == 0: | ||||||
|  |             return interpret(b, x, argv=argv, outs=outs) | ||||||
|  |         else: | ||||||
|  |             return interpret(i + 3, x, argv=argv, outs=outs) | ||||||
|  |         pass | ||||||
|  |     # less than | ||||||
|  |     elif op == '07': | ||||||
|  |         a, b, out = x[i + 1], x[i + 2], x[i + 3] | ||||||
|  |         mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[ | ||||||
|  |             0] | ||||||
|  |         a = a if mode_a == VAL else x[a] | ||||||
|  |         b = b if mode_b == VAL else x[b] | ||||||
|  |         assert mode_out == POS | ||||||
|  |         if a < b: | ||||||
|  |             x[out] = 1 | ||||||
|  |         else: | ||||||
|  |             x[out] = 0 | ||||||
|  |         return interpret(i + 4, x, argv=argv, outs=outs) | ||||||
|  |     # equals | ||||||
|  |     elif op == '08': | ||||||
|  |         a, b, out = x[i + 1], x[i + 2], x[i + 3] | ||||||
|  |         mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[ | ||||||
|  |             0] | ||||||
|  |         a = a if mode_a == VAL else x[a] | ||||||
|  |         b = b if mode_b == VAL else x[b] | ||||||
|  |         assert mode_out == POS | ||||||
|  |         if a == b: | ||||||
|  |             x[out] = 1 | ||||||
|  |         else: | ||||||
|  |             x[out] = 0 | ||||||
|  |         return interpret(i + 4, x, argv=argv, outs=outs) | ||||||
|  |     elif op == '99': | ||||||
|  |         return x[0] | ||||||
|  |     else: | ||||||
|  |         raise Exception('Unsupported opcode: {}.'.format(op)) | ||||||
							
								
								
									
										
											BIN
										
									
								
								universe/advent-of-code/day_5.pyc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								universe/advent-of-code/day_5.pyc
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										155
									
								
								universe/advent-of-code/day_6.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								universe/advent-of-code/day_6.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,155 @@ | ||||||
|  | from graphviz import Digraph | ||||||
|  | 
 | ||||||
|  | data = """6WF)DRK 2PT)PSM H42)FN8 1XR)LQD HRK)9KL TD6)H8W 98Z)BJM RCQ)LVG | ||||||
|  | RWQ)Q7H 2PS)X94 NHB)25X PXC)W57 L8L)MVX CFK)D8K R1B)43T PDY)QKX FQK)82K JJ6)MQJ | ||||||
|  | FB6)6V1 R28)5MZ BN2)5HN 6BQ)JVC W57)22C MQJ)DL2 MTC)84R RH8)CRN Y27)3GN CKQ)31C | ||||||
|  | R7V)9BK ZDY)PDY X2Q)Y6S Q8B)SAN 1Z3)PVT R87)57R KCJ)44X PWQ)9CB HLC)VYW HFP)9XS | ||||||
|  | X33)MC3 RYS)R7R JRF)VHW 79R)FXZ YQQ)STV 8J6)JWX Q6D)RV6 LL9)B4D 6R1)T1Z VK9)42M | ||||||
|  | PQP)17N K6C)HMK GLY)N47 KDW)CDC DQ4)RY5 SND)FDR 7YF)1VN MDT)B3S D3F)98Z 5VH)MR7 | ||||||
|  | KNR)2L8 CJW)QDL FWY)14X SJD)79R COM)BXW T2B)FPB B2Q)BRJ Z21)HYC VHW)5XR WZ4)2JM | ||||||
|  | 8HF)342 PYR)X9Y RKF)P43 S1S)9WT 2PB)BSB QF7)M9T HML)HMC 7J9)7Q6 8F1)29K DH1)NDM | ||||||
|  | 1YC)PXC P32)HR7 PMX)7Y9 STV)SLW NYY)NF1 TG9)998 DMB)DLW XGL)1Z3 GK8)WCS YHR)HQC | ||||||
|  | 9Q5)B6D R2T)CM5 6KC)J5G ZM9)L8L J8T)F89 3LN)YOU T2T)Z8F SCY)FKG 9W4)195 QLM)DD7 | ||||||
|  | 4QY)JCB WKM)3JF 693)YM8 61M)B6Y DSP)X2M YZ5)DPL BC9)3B1 BDB)JTG 3TJ)TW1 W5M)SF6 | ||||||
|  | K4Q)X56 5HT)YHX YJG)DM5 68N)X2Q 2YP)DS5 BLK)MY3 6WV)VZ4 2JQ)ZT8 G93)V2W WN1)SBD | ||||||
|  | SS7)DY9 X56)8HP JY1)VS4 XQ6)L94 98Z)DMC V6S)NWT D9L)Y44 V6G)GVS JDW)FZW FJT)S38 | ||||||
|  | L2Z)VPL 7ZX)DKK X2M)8WM YVZ)XWS HMK)P87 47M)TD6 TDZ)21T 19R)95B GD9)Q1L 9QX)DFR | ||||||
|  | Y64)XGN CRG)6VY V3L)61D RJ4)C9Z XXG)P53 VJ8)QTF CPQ)2M9 JRN)8V1 KMH)K94 DLW)VQ4 | ||||||
|  | 91W)2QQ G4B)RWQ 4P1)MKS K6G)DZ7 WCS)JR9 LXM)7RY 6ZB)K6G HMC)622 Z21)BLK Q6N)48V | ||||||
|  | 66S)MK4 PDK)6WV Y6S)GY1 2L8)ZMG 42W)ZN6 6MS)8TZ JBY)STQ NSF)3ZM 5CV)X9N K4V)WFL | ||||||
|  | J6R)DT8 N3N)CX4 PTD)YXT F74)4T5 C51)3FW KRW)DS1 NWT)CKQ 195)6G6 HVQ)S18 Q7H)BKM | ||||||
|  | SKN)4D4 GK2)MLX MVX)TG9 YPK)RHQ Y9F)Z8W 42M)WNL 84R)6JP KNC)NHF FZW)PGM 3FW)HGX | ||||||
|  | DBK)FB6 45T)HLT L11)JVN HB5)K6C QH5)888 BTJ)J55 8BT)8ZS FR1)XGL S87)PS9 C4K)BN2 | ||||||
|  | N2Q)18C KTF)ZM9 TN2)B2Q DF3)CFK 9T3)TMR P29)3P1 P1W)7SQ 4D4)1DJ LML)ZJ3 Q4L)RKF | ||||||
|  | MW2)79T LVG)CPQ BDC)JH5 DNZ)232 998)GTM YGS)4WH GY1)C51 J55)QBT B8Z)34W FJ2)H42 | ||||||
|  | 58J)326 T1Z)DCJ 1ZH)GLV 1YC)JG6 14K)22B RY5)QRY 7V2)2WT 4GQ)XHV ZJ3)TQ8 2G8)SN3 | ||||||
|  | FPB)HMN SC4)57D 5LQ)R2T LXM)R8Z JQ6)G4B WNL)GK2 42M)P75 LM3)YPK ZN6)753 PN4)835 | ||||||
|  | C4H)JY1 LR4)VD5 PSM)P1W VWL)C6C G2V)WBC 85M)R24 B1V)QW7 175)2PM Y1V)1ZH 34W)3MJ | ||||||
|  | WN7)TTB 3PV)CQD N7Y)9T3 223)8D4 RV6)LJ9 HFP)JRF VMT)DNB GJP)D3F J5G)KMS 7Q6)ZW2 | ||||||
|  | YCB)JBY XGN)MNL 888)DSP X61)Q6N WT5)X12 SDN)FD1 2QC)54W V98)964 T7S)YVZ MLX)9VZ | ||||||
|  | FR8)QH5 TVQ)2PS 2PV)FHY F4S)MPT 3J9)JNB J6M)GDC Q4C)MJN 9VZ)BZK P2P)B69 WBC)M1W | ||||||
|  | D97)HPF JKB)9L4 593)6YJ RMB)4Q5 QZB)38C H12)6R1 MKY)DDD HGX)CRG P53)WY7 22B)GMM | ||||||
|  | 44X)2D8 DT8)L7H 3Y2)D3S FB8)68N 3BC)1XR 4XF)TVQ VPL)R7V Z4V)JSK B3S)FW5 49Z)YQQ | ||||||
|  | 99V)D13 54Q)SS7 CYC)TXH PQ3)78W X4M)G9H WFL)M99 ZYY)3Y2 12Y)PSW W38)P29 H8W)JJ6 | ||||||
|  | P66)VPH GK2)45T H5F)FJT JDJ)SNV 14F)96Q JG6)TQ4 2L6)52Q SCY)CBJ 3GN)KNC KLM)XPR | ||||||
|  | DH1)QZB DMB)X7G DPL)7SX D97)N3N GNS)T95 53P)GW2 BHR)HNB YHX)XQV 2CR)Y1V C9D)Z7P | ||||||
|  | FN8)2PT 6LF)FCQ JNL)LQR SPV)YCB HGX)N83 VS4)8BT 5RH)FTX HYC)X2J 69V)J6S 9XS)PN4 | ||||||
|  | SD7)5Q3 2RN)82D QRY)FFY K2Y)3X2 79Z)S2Z YN2)Y64 JKB)MDT KJ8)NDH N57)5VH 3XK)1Q1 | ||||||
|  | SCH)FJ6 17N)GMP QR4)7V2 GLV)GLY NHF)ZDY QDL)S14 QF1)BMC ZLF)DHN 3JF)7TR MKS)GCY | ||||||
|  | 964)91R 9L4)L5G RRX)6ZB CD7)73M 3X2)PGC HNB)S9Z L94)KLM 8MQ)SCR 18C)3TJ M4Y)BTJ | ||||||
|  | BC9)5YR TV5)SCY 2NX)8CC C9Z)MTC B69)3QP HR7)CHJ 8ZS)JRN 31C)TJW D43)4NH 93Q)X9X | ||||||
|  | T95)DNZ LQ5)BC9 9T5)S2C RP8)DH1 GCY)SD7 Y44)9B5 VG5)ZYY 7RY)V3L PWV)Q4L NF1)7YF | ||||||
|  | DRK)Y8V D13)GYG TW1)2PB ZVZ)2VV BRJ)V2V 9CB)Y7B MK4)9CJ TMR)6XS HWF)GK8 QTF)S1S | ||||||
|  | DFW)6LF N3S)WN1 N2Q)MSW CZ5)X61 FXZ)C4H SCQ)MF7 9LY)3LN 5MZ)PMX CN9)WF9 FHY)PR8 | ||||||
|  | S38)NWH M29)G5S 4NH)GZJ 5YR)54H CLX)MNY TJD)HQL RRZ)4GQ YHB)CZ5 P37)93Q YJG)3Q3 | ||||||
|  | 95B)QMF CMQ)BLZ QD9)45M JSK)R28 YCW)CLX 8K3)JGB N8M)PQW P75)1HL XBS)T2T 22C)PVW | ||||||
|  | 689)6MS FFY)RWX YHL)2G8 Y8V)4P1 Y7B)62Z YKJ)JDJ 1HL)5LQ PZ3)B1C 52Q)7HB 3Q2)ZV7 | ||||||
|  | YBF)Z4V J95)SDH NM6)YBF 8YN)J3M J6S)KNR PVT)N4X SDH)RFW RFW)7Y1 JCB)52B 3MJ)H58 | ||||||
|  | 4QF)XHZ F62)DFW 7LJ)KDW JHL)C9D B4D)Q8B 342)YGS PFR)ZQT Z9K)TNS 8F8)WLB 94N)DMB | ||||||
|  | QBT)RYS 3VR)KRR 8D4)ST6 X9N)2PV 632)8K3 MX5)XNP 57D)Y27 18D)PQP D3F)RJ4 PLS)PBL | ||||||
|  | 1JP)YDC 79V)BG2 S14)2NX 4Q5)NCQ FTX)555 2PM)KMH HQC)RMB 9Z9)BNZ XHV)Y94 7ZP)YHR | ||||||
|  | BNZ)49Z W6D)LX6 SLS)JL3 PVW)P9W Z1L)HB5 DS5)G2V Z9Q)RV8 DFR)LPJ 836)693 K94)VWL | ||||||
|  | HRG)836 J3V)593 52N)LPK 9KL)Y7M LX6)F7D JL3)511 L4G)D97 1RH)Y9F NJ2)LML GW2)9WV | ||||||
|  | 8KZ)NRC XQV)G6D R8Z)QF7 326)HML R7R)8PM 622)YCW WQY)LGS NF1)FF3 5LQ)QF1 5XR)PTD | ||||||
|  | V2V)PFR 9T5)JQ6 CBQ)8KZ VZ4)HVQ TJW)DQT 9WT)5M6 CFK)YHL JR9)1JP Y1K)CF4 8WS)JPY | ||||||
|  | VYC)1D6 GKK)7J9 JTG)RRX 6V1)F74 1H5)QR4 SN3)NMG MF7)GQ1 RYK)SCH BNZ)9LY 1DJ)9LP | ||||||
|  | L6W)5BK FCQ)BFL DCJ)3RD MXD)8MQ RWX)1RH NBF)WKM K6C)WNH H58)L6W Y7B)BJH PGC)NBF | ||||||
|  | 96Q)Q2W F7D)BSN 223)Z9K K94)VYC X9X)7M3 Q1M)3J9 QXF)XQ6 DD7)3Q2 Q1L)NHB 79T)LXQ | ||||||
|  | 8TZ)M29 21T)Q4C B1C)NSF 8D8)FJ2 LJH)HGJ QS2)PS1 5KX)Z2L C6C)6BQ VQ2)2YP P87)N8M | ||||||
|  | ST5)L4G 8SP)W5M T4H)69V 9WF)GHS FF3)SND C5G)GKK VQ2)X4M P43)8J6 TD6)384 66V)CN9 | ||||||
|  | CX4)T9T NCQ)2JQ 29K)K8K RY5)K4Q GQ3)T4H FNH)P32 3BC)PRQ 5HN)4QY M1W)BGT 84R)ST5 | ||||||
|  | S45)CJW CK4)W7G SGX)19R S2C)7ZX DHN)W5Y 8D9)HM2 BSB)SPV D8K)DFV JHL)2L6 KYP)12Y | ||||||
|  | KDN)6X7 Y44)SQZ 6G6)SJD N7D)QGF Q84)8WJ F89)LL9 LYJ)2RN 25X)Q84 HM3)53P JNB)QD9 | ||||||
|  | SLW)1DQ 384)3BC PR8)NGV 49N)7ZP 65H)LHJ 6XS)S45 ZMG)FR1 X2M)Y86 QD3)QLM P4R)PQ3 | ||||||
|  | RTK)4M3 4YW)N7D R7V)M4M 73M)CBF DFV)64R Z7P)LMK HRG)Y1K 3ZM)BCZ WY7)QXP DMC)9Q5 | ||||||
|  | PSW)1H5 8CC)TV5 TTB)S88 BZK)K2Y T2B)CBQ HJB)Y19 DQW)KML Z8W)8ZL PBL)5TK 1D6)MX5 | ||||||
|  | 3MJ)4YW MDT)HJB 62Z)X33 DZ7)BDC 9CJ)FRD 82D)KDN LK7)18D 9QQ)61M Y34)DZG J4T)6KC | ||||||
|  | 971)QD3 511)GQ3 MJN)F62 RNM)NKG BGW)KJ8 DL2)1YH ZQT)RYZ 1YH)ZJ6 2WT)YYQ 7HB)DYQ | ||||||
|  | 3BN)WQY 2M9)62D TSK)YR1 N7Y)VJ8 WZ4)FWT MNY)YN2 DYQ)RRZ 3RG)YT3 2SM)VK9 JH5)ZXH | ||||||
|  | GYG)K2M PKF)V6G JGB)S87 X94)N57 MSW)L2Z X4N)25G BLZ)4QF JPY)GD9 WLB)V6S KML)2SM | ||||||
|  | TXH)9X1 48V)KTR 8PM)WZ4 ZW2)967 PS9)3BN 4WH)9T5 8M1)R6V N7M)VWK S88)978 N4X)8KH | ||||||
|  | 6VY)PLS NRC)874 QGF)QWJ NMG)J3V B8Z)WPF 45M)2QC KDW)VQ2 FZW)223 BXW)QXF FRD)PWV | ||||||
|  | 8HP)4G7 KDN)YYL LHJ)SDN P6P)XMC W5Y)RYK HX8)KW3 Z2L)H12 WPF)T2B L7H)BGW MNL)17B | ||||||
|  | GHS)66V QKX)XWV FW5)W38 PDK)Y34 FKG)Q6D DQT)YJG 15G)79V 4VK)51Y BJH)LR4 48V)6GC | ||||||
|  | DM5)Y1F CM5)VG5 KB8)HRK 5HN)RCQ 6JP)SDQ LGH)NJ2 L94)N7Y 4Y2)ZLF 25G)C4K K8K)SLS | ||||||
|  | 232)ZVZ GQ1)58J RV8)H5F 78W)565 YCF)8D9 DZG)99V N83)CKR TN2)ZCX NGV)8SP BSN)FTN | ||||||
|  | LPJ)94N 3Q3)Q1M JVX)971 54W)LGH 67Y)P66 R24)P37 3QP)QTY YHR)FLT GMP)NM6 NDH)632 | ||||||
|  | PWV)8D8 LMK)3PV ZWJ)KB8 967)4VK 3B1)WN7 XWS)5CV YR1)FNH 565)4PH 5BK)V98 W5Y)FR8 | ||||||
|  | PS1)HX8 38C)XXG XWV)1YC M4M)LQ5 S9Z)49N XMC)R1B YYL)VC9 GMM)SCQ LXQ)J95 51Y)RP8 | ||||||
|  | HLT)XBS 82K)B8Z NR5)7K3 K2M)67Y SF6)W6D CF4)85M MC3)LXM HMN)RNM BFL)4XF MT2)PM4 | ||||||
|  | VWK)JKB 3JF)ZTZ QWJ)9QQ KRR)TJD VYW)Z9Q CK4)QS2 8NQ)NR5 57R)BHR 8WM)YHB Y86)GNS | ||||||
|  | 2Y2)Z21 X12)9QX LJ9)YKJ 3RD)8F1 7SQ)CK4 ZXH)3XK DDD)5KX ZCX)PYR GZJ)KXL KC5)52N | ||||||
|  | PM4)RYP 14X)ZWJ FJ6)175 17B)689 HQL)14F LQR)DBK LGS)4Y2 2QQ)SGR 2VV)8F8 J6S)LM3 | ||||||
|  | RTP)YZ5 XDD)14K VQ4)MT2 KMH)KYC CKR)RTP VD5)MRM CM5)KRW BG3)XDD PGM)J4T MY3)JVX | ||||||
|  | Z8F)WNP BKM)WT5 FLT)KTF N7D)8M1 Y19)CMQ HPF)WDL 65H)JJP 2MQ)66S 4Q5)54Q Q2W)ZL4 | ||||||
|  | QTY)659 MRM)9Z9 X2J)SC4 YWH)RB3 FTN)LYJ LMK)N7M SGX)15G KW3)FQK 3VV)JNL JWX)R8R | ||||||
|  | 9Z3)9MB BMC)N3S W7G)Z1L SD7)MW2 376)RH8 NWT)JHL 7CD)N2Z KTR)HM3 1Q1)TDZ DY9)2CR | ||||||
|  | 6YJ)14G FWT)JDW C2S)C5G SNV)J6M 5TK)YWH J3M)8HF HM2)GJP P9W)7CD 1VN)SGX KMS)RBK | ||||||
|  | 64R)B1V 62D)3VV 61D)F4S XPR)SKN FJT)N3P 9WV)D43 TQ8)BDB 46H)K4V 8WJ)MXD NDM)9WF | ||||||
|  | 8ZL)1QJ SCR)2MQ 7Y9)LJH VPH)MKY YDC)PDK 4G7)65H 2JM)NYY T9T)VMT 8M1)TSK G5S)X4N | ||||||
|  | 6FH)KYP D98)DQW G6D)C2S 6X7)N2Q 1QJ)T7S ZL4)J8T 5BT)3VR 835)KCJ YM8)3RG Y7M)PWQ | ||||||
|  | 54W)9W4 CBF)7LJ 4T5)8WS RHQ)HBK CQD)D98 HGJ)J6R JVC)79Z FD1)PKF VC9)5BT C4H)6WF | ||||||
|  | D3S)P6P MR7)BG3 R6V)DF3 9X1)NQ5 ZTZ)2Y2 8WM)HFP CDC)376 TQ4)M4Y 9MB)N1R HBK)DQ4 | ||||||
|  | 1DQ)CYC WNP)DM8 CBJ)LK7 ZT8)FWY LQD)PNN 555)9Z3 TNS)D9L QMF)L11 FR8)5RH WF9)R87 | ||||||
|  | NKG)5HT L5G)91W N2Z)YV9 9B5)CD7 ZV7)8NQ ST6)74T ZJ6)CQV S18)47M 74T)8YN WNH)TN2 | ||||||
|  | 874)46H 3VV)PZ3 Y1F)42W MPT)2LP FDR)HWF X7G)RTK 52B)P4R RYP)G93 NWH)YCF 7TR)FB8 | ||||||
|  | RWQ)6FH 8F8)HLC CRN)P2P B6D)KC5 PNN)HRG""".split() | ||||||
|  | 
 | ||||||
|  | # COM is the root in this tree | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # parent :: Vertex -> [Edge] -> Maybe(Vertex) | ||||||
|  | def parent(x, xs): | ||||||
|  |     for a, b in xs: | ||||||
|  |         if b == x: | ||||||
|  |             return a | ||||||
|  |     return None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # parents :: Vertex -> [Edge] -> [Vertex] | ||||||
|  | def parents(x, xs): | ||||||
|  |     parents = [] | ||||||
|  |     p = parent(x, xs) | ||||||
|  |     while p: | ||||||
|  |         parents.append(p) | ||||||
|  |         p = parent(p, xs) | ||||||
|  |     return parents | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # alias Vertex :: String | ||||||
|  | # alias Edge :: (String, String) | ||||||
|  | # to_edge_list :: [String] -> [(String, String)] | ||||||
|  | def to_edge_list(xs): | ||||||
|  |     """Returns a list of tuples where (A, B) represents a directed edge from | ||||||
|  |     vertex A to vertex B.""" | ||||||
|  |     return [(x[0:3], x[4:]) for x in xs] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # to_graphviz :: [Edge] -> String | ||||||
|  | def to_graphviz(xs): | ||||||
|  |     d = Digraph() | ||||||
|  |     for a, b in xs: | ||||||
|  |         d.node(a, label=a) | ||||||
|  |         d.edge(a, b) | ||||||
|  |     return d.source | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | graph = to_edge_list(data) | ||||||
|  | you = parents('YOU', graph) | ||||||
|  | san = parents('SAN', graph) | ||||||
|  | 
 | ||||||
|  | # Distance from YOU to shared point with SAN | ||||||
|  | yd = 1 | ||||||
|  | for i in range(len(you)): | ||||||
|  |     if you[i] in san: | ||||||
|  |         break | ||||||
|  |     yd += 1 | ||||||
|  | 
 | ||||||
|  | # Distance from SAN to shared point with YOU | ||||||
|  | sd = 1 | ||||||
|  | for i in range(len(san)): | ||||||
|  |     if san[i] in you: | ||||||
|  |         break | ||||||
|  |     sd += 1 | ||||||
|  | 
 | ||||||
|  | print('Number of orbital transfers required: {}'.format(yd - 1 + sd - 1)) | ||||||
							
								
								
									
										49
									
								
								universe/advent-of-code/day_7.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								universe/advent-of-code/day_7.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | from day_5 import interpret | ||||||
|  | from itertools import permutations | ||||||
|  | 
 | ||||||
|  | # TODO: I may need to re-write this in Elixir modelling each amplifier as a | ||||||
|  | # `Process` and `Process.send`ing each amplifier the signals. | ||||||
|  | 
 | ||||||
|  | data = [ | ||||||
|  |     3, 8, 1001, 8, 10, 8, 105, 1, 0, 0, 21, 38, 59, 76, 89, 106, 187, 268, 349, | ||||||
|  |     430, 99999, 3, 9, 1002, 9, 3, 9, 101, 2, 9, 9, 1002, 9, 4, 9, 4, 9, 99, 3, | ||||||
|  |     9, 1001, 9, 5, 9, 1002, 9, 5, 9, 1001, 9, 2, 9, 1002, 9, 3, 9, 4, 9, 99, 3, | ||||||
|  |     9, 1001, 9, 4, 9, 102, 4, 9, 9, 1001, 9, 3, 9, 4, 9, 99, 3, 9, 101, 4, 9, | ||||||
|  |     9, 1002, 9, 5, 9, 4, 9, 99, 3, 9, 1002, 9, 3, 9, 101, 5, 9, 9, 1002, 9, 3, | ||||||
|  |     9, 4, 9, 99, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, | ||||||
|  |     1002, 9, 2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, | ||||||
|  |     3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, | ||||||
|  |     9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 99, 3, 9, 1002, 9, | ||||||
|  |     2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, | ||||||
|  |     1, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, | ||||||
|  |     101, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, | ||||||
|  |     9, 1001, 9, 2, 9, 4, 9, 99, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, | ||||||
|  |     4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9, | ||||||
|  |     9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 102, 2, | ||||||
|  |     9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 99, 3, 9, | ||||||
|  |     1001, 9, 2, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, | ||||||
|  |     3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, | ||||||
|  |     9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, 1, 9, | ||||||
|  |     9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 99, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 102, | ||||||
|  |     2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, | ||||||
|  |     1001, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, | ||||||
|  |     3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 102, 2, 9, 9, | ||||||
|  |     4, 9, 99 | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | data_a, data_b, data_c, data_d, data_e = data[:], data[:], data[:], data[:], data[:] | ||||||
|  | 
 | ||||||
|  | # m = 0 | ||||||
|  | # for a, b, c, d, e in permutations(range(5, 10)): | ||||||
|  | #     answer = None | ||||||
|  | #     z = 0 | ||||||
|  | #     while z is not None: | ||||||
|  | #         print(a, b, c, d, e) | ||||||
|  | #         print('---') | ||||||
|  | #         v = interpret(0, data_a, argv=[a, z]) | ||||||
|  | #         print(v) | ||||||
|  | #         w = interpret(0, data_b, argv=[b, v]) | ||||||
|  | #         x = interpret(0, data_c, argv=[c, w]) | ||||||
|  | #         y = interpret(0, data_d, argv=[d, x]) | ||||||
|  | #         z = interpret(0, data_e, argv=[e, y]) | ||||||
|  | #         m = max(m, z) | ||||||
							
								
								
									
										18
									
								
								universe/advent-of-code/writePythonBin.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								universe/advent-of-code/writePythonBin.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | { pkgs ? import <nixpkgs> {}, ... }: | ||||||
|  | 
 | ||||||
|  | { name, deps, src }: | ||||||
|  | 
 | ||||||
|  | let | ||||||
|  |   inherit (pkgs) pythonPackages writeTextFile; | ||||||
|  |   inherit (builtins) toFile; | ||||||
|  | 
 | ||||||
|  | in writeTextFile { | ||||||
|  |   inherit name; | ||||||
|  |   executable = true; | ||||||
|  |   destination = "/bin/${name}"; | ||||||
|  | 
 | ||||||
|  |   text = '' | ||||||
|  |     #!/bin/sh | ||||||
|  |     ${pkgs.python3}/bin/python3 ${src} | ||||||
|  |   ''; | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								universe/blog/default.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								universe/blog/default.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | { | ||||||
|  |   nixpkgs ? import <nixpkgs> {}, | ||||||
|  |   depot ? import <depot> {}, | ||||||
|  |   universe ? import <universe> {}, | ||||||
|  |   ... | ||||||
|  | }: | ||||||
|  | 
 | ||||||
|  | let | ||||||
|  |   injectedPosts = nixpkgs.writeText "posts.lisp" '' | ||||||
|  |     (in-package #:server) | ||||||
|  |     (setq *path-to-posts* "${./posts}") | ||||||
|  |   ''; | ||||||
|  |   injectedExecutables = nixpkgs.writeText "executables.lisp" '' | ||||||
|  |     (in-package #:server) | ||||||
|  |     (setq *pandoc-bin* "${nixpkgs.pandoc}/bin/pandoc") | ||||||
|  |   ''; | ||||||
|  | in depot.nix.buildLisp.program { | ||||||
|  |   name = "server"; | ||||||
|  |   deps = with depot.third_party.lisp; with universe.third_party.lisp; [ | ||||||
|  |     hunchentoot | ||||||
|  |     cl-arrows | ||||||
|  |   ]; | ||||||
|  |   srcs = [ | ||||||
|  |     ./src/server.lisp | ||||||
|  |     injectedPosts | ||||||
|  |     injectedExecutables | ||||||
|  |   ]; | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								universe/blog/posts/test.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								universe/blog/posts/test.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | # Testing | ||||||
|  | 
 | ||||||
|  | The goal here is to be able to write markdown files and have a server that can | ||||||
|  | render the markdown into HTML and serve them to the clients. | ||||||
							
								
								
									
										16
									
								
								universe/blog/src/index.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								universe/blog/src/index.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="utf-8" /> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||||
|  |     <meta name="description" content="Showcase of AI bots at DeepMind" /> | ||||||
|  |     <link rel="stylesheet" href="/index.css"> | ||||||
|  |     <title>AI Showcase</title> | ||||||
|  |     <script data-ad-client="ca-pub-6018268443649487" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <noscript>You need to enable JavaScript to run this app.</noscript> | ||||||
|  |     <h1>Welcome</h1> | ||||||
|  |     <p>To my blog!</p> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										49
									
								
								universe/blog/src/server.lisp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								universe/blog/src/server.lisp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | (in-package #:cl-user) | ||||||
|  | (defpackage #:server | ||||||
|  |   (:documentation "Robot condemned to a life of admin work for my blog.") | ||||||
|  |   (:use #:cl) | ||||||
|  |   (:import-from #:cl-arrows #:->>) | ||||||
|  |   (:export :main)) | ||||||
|  | (in-package #:server) | ||||||
|  | 
 | ||||||
|  | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
|  | ;; Nix-injected dependencies | ||||||
|  | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
|  | 
 | ||||||
|  | ;; TODO: Wrap this in an assert or ensure that there's a trailing slash so it's | ||||||
|  | ;; treated as a directory. | ||||||
|  | (defvar *path-to-posts* "/tmp/" | ||||||
|  |   "File path pointing to the posts directory.") | ||||||
|  | 
 | ||||||
|  | (defvar *pandoc-bin* "/usr/bin/pandoc") | ||||||
|  | 
 | ||||||
|  | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
|  | ;; Library | ||||||
|  | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
|  | 
 | ||||||
|  | (defun render-post (path) | ||||||
|  |   "Render the markdown file stored at PATH to HTML using pandoc." | ||||||
|  |   (uiop:run-program (list *pandoc-bin* path "--to" "html") | ||||||
|  |                     :output :string)) | ||||||
|  | 
 | ||||||
|  | ;; TODO: Figure out how to handle this with Nix. | ||||||
|  | (defvar *posts* (uiop:directory-files *path-to-posts*) | ||||||
|  |   "List of the paths to the blog posts.") | ||||||
|  | 
 | ||||||
|  | (hunchentoot:define-easy-handler | ||||||
|  |     (get-latest :uri "/latest") () | ||||||
|  |   (render-post (concatenate 'string *path-to-posts* "/" "test.md"))) | ||||||
|  | 
 | ||||||
|  | (hunchentoot:define-easy-handler | ||||||
|  |     (get-posts :uri "/posts") () | ||||||
|  |   "Working!") | ||||||
|  | 
 | ||||||
|  | (defun main () | ||||||
|  |   "This is the main entrypoint for our application." | ||||||
|  |   (hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 4242)) | ||||||
|  |   (print "Listing on port 4242...") | ||||||
|  |   (sb-thread:join-thread | ||||||
|  |    (find-if (lambda (th) | ||||||
|  |               (string= (sb-thread:thread-name th) | ||||||
|  |                        "hunchentoot-listener-*:4242")) | ||||||
|  |             (sb-thread:list-all-threads)))) | ||||||
							
								
								
									
										11
									
								
								universe/clojure/buildClojure.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								universe/clojure/buildClojure.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | { universe ? import <universe> {}, ... }: | ||||||
|  | 
 | ||||||
|  | universe.nix.buildClojure.program { | ||||||
|  |   name = "test"; | ||||||
|  |   deps = with universe.third_party.clojure; [ | ||||||
|  | 
 | ||||||
|  |   ]; | ||||||
|  |   srcs = [ | ||||||
|  |     ./main.clj | ||||||
|  |   ] | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								universe/crack_the_coding_interview/11_1.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								universe/crack_the_coding_interview/11_1.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | ||||||
|  | # Implementation for a problem from "Crack the Coding Interview". | ||||||
|  | # | ||||||
|  | # Dependencies: | ||||||
|  | # - python 2.7.16 | ||||||
|  | # - entr 4.1 | ||||||
|  | # | ||||||
|  | # To run the tests, run: `python 11_1.py` | ||||||
|  | # For a tight development loop, run: `echo 11_1.py | entr python /_` | ||||||
|  | # | ||||||
|  | # Author: William Carroll <wpcarro@gmail.com> | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Implementation | ||||||
|  | ################################################################################ | ||||||
|  | def insert_sorted(xs, ys): | ||||||
|  |     """ | ||||||
|  |     Merges `ys` into `xs` and ensures that the result is sorted. | ||||||
|  | 
 | ||||||
|  |     Assumptions: | ||||||
|  |     - `xs` and `ys` are both sorted. | ||||||
|  |     - `xs` has enough unused space to accommodate each element in `ys`. | ||||||
|  |     """ | ||||||
|  |     for y in ys: | ||||||
|  |         xi = xs.index(None) - 1 | ||||||
|  |         yi = xs.index(None) | ||||||
|  |         xs[yi] = y | ||||||
|  |         while xi != -1 and y < xs[xi]: | ||||||
|  |             xs[xi], xs[yi] = xs[yi], xs[xi] | ||||||
|  |             xi, yi = xi - 1, yi - 1 | ||||||
|  |     return xs | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | assert insert_sorted([1, 3, 5, None, None], [2, 4]) == [1, 2, 3, 4, 5] | ||||||
|  | assert insert_sorted([None, None], [2, 4]) == [2, 4] | ||||||
|  | assert insert_sorted([None, None], [2, 4]) == [2, 4] | ||||||
|  | assert insert_sorted([1, 1, None, None], [0, 0]) == [0, 0, 1, 1] | ||||||
|  | assert insert_sorted([1, 1, None, None], [1, 1]) == [1, 1, 1, 1] | ||||||
|  | print('All tests pass!') | ||||||
							
								
								
									
										11
									
								
								universe/crack_the_coding_interview/to_tree.hs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								universe/crack_the_coding_interview/to_tree.hs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | data Tree a = Node a [Tree a] deriving (Show) | ||||||
|  | 
 | ||||||
|  | withRoot :: [a] -> [Tree a] | ||||||
|  | withRoot xs = xs |> toThing |> fmap buildTree | ||||||
|  | 
 | ||||||
|  | buildTree :: (a, [a]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | toTree :: [a] -> Tree a | ||||||
|  | toTree [x]      = Node x [] | ||||||
|  | toTree [x | xs] = Node x (toTree xs) | ||||||
							
								
								
									
										87
									
								
								universe/data_structures_and_algorithms/array-traversals.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								universe/data_structures_and_algorithms/array-traversals.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | ||||||
|  | # This is practice for various types of list traversals that turn up. | ||||||
|  | 
 | ||||||
|  | xs = range(10) | ||||||
|  | n = len(xs) | ||||||
|  | 
 | ||||||
|  | print('---') | ||||||
|  | # pythonic left-to-right traversal | ||||||
|  | result = '' | ||||||
|  | for x in xs: | ||||||
|  |     result += str(x) | ||||||
|  | print(result) | ||||||
|  | 
 | ||||||
|  | print('---') | ||||||
|  | # left-to-right traversal | ||||||
|  | result = '' | ||||||
|  | for i in range(n): | ||||||
|  |     result += str(xs[i]) | ||||||
|  | print(result) | ||||||
|  | 
 | ||||||
|  | print('---') | ||||||
|  | # right-to-left traversal | ||||||
|  | result = '' | ||||||
|  | for i in range(n): | ||||||
|  |     result += str(xs[n - 1 - i]) | ||||||
|  | print(result) | ||||||
|  | 
 | ||||||
|  | print('---') | ||||||
|  | # 2x left-to-right traversal | ||||||
|  | result = '' | ||||||
|  | for i in range(2 * n): | ||||||
|  |     result += str(xs[i % n]) | ||||||
|  | print(result) | ||||||
|  | 
 | ||||||
|  | print('---') | ||||||
|  | # 2x right-to-left traversal | ||||||
|  | result = '' | ||||||
|  | for i in range(2 * n): | ||||||
|  |     result += str(xs[(n - 1 - i) % n]) | ||||||
|  | print(result) | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Table traversals | ||||||
|  | ################################################################################ | ||||||
|  | 
 | ||||||
|  | table = [[row * 10 + i for i in range(10)] for row in range(3)] | ||||||
|  | row_ct = len(table) | ||||||
|  | col_ct = len(table[0]) | ||||||
|  | 
 | ||||||
|  | print('---') | ||||||
|  | # 3x10 table traversal | ||||||
|  | result = '' | ||||||
|  | for row in table: | ||||||
|  |     r = '' | ||||||
|  |     for col in row: | ||||||
|  |         r += '{:3d}'.format(col) | ||||||
|  |     result += r + '\n' | ||||||
|  | print(result[0:-1]) | ||||||
|  | 
 | ||||||
|  | print('---') | ||||||
|  | # 3x10 table traversal | ||||||
|  | result = '' | ||||||
|  | for row in range(row_ct): | ||||||
|  |     r = '' | ||||||
|  |     for col in range(col_ct): | ||||||
|  |         r += '{:3d}'.format(table[row][col]) | ||||||
|  |     result += r + '\n' | ||||||
|  | print(result[0:-1]) | ||||||
|  | 
 | ||||||
|  | print('---') | ||||||
|  | # 3x10 table traversal (reverse) | ||||||
|  | result = '' | ||||||
|  | for row in range(row_ct): | ||||||
|  |     r = '' | ||||||
|  |     for col in range(col_ct): | ||||||
|  |         r += '{:3d}'.format(table[row_ct - 1 - row][col_ct - 1 - col]) | ||||||
|  |     result += r + '\n' | ||||||
|  | print(result) | ||||||
|  | 
 | ||||||
|  | print('---') | ||||||
|  | # 3x10 column-row traversal | ||||||
|  | result = '' | ||||||
|  | for col in range(col_ct): | ||||||
|  |     r = '' | ||||||
|  |     for row in range(row_ct): | ||||||
|  |         r += '{:3d}'.format(table[row][col]) | ||||||
|  |     result += r + '\n' | ||||||
|  | print(result) | ||||||
							
								
								
									
										145
									
								
								universe/data_structures_and_algorithms/balanced-binary-tree.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								universe/data_structures_and_algorithms/balanced-binary-tree.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,145 @@ | ||||||
|  | import unittest | ||||||
|  | from itertools import combinations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def balanced(xs): | ||||||
|  |     """Return True if `xs` contains no two values that differ by more than | ||||||
|  |     one.""" | ||||||
|  |     if len(xs) == 0 or len(xs) == 1: | ||||||
|  |         return True | ||||||
|  |     if len(xs) == 2: | ||||||
|  |         return math.abs(xs[0] - xs[1]) <= 1 | ||||||
|  |     else: | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def is_leaf(node): | ||||||
|  |     return node.left is None and node.right is None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def is_balanced(tree_root): | ||||||
|  |     """Returns True if the difference between the depths of any two leaf nodes | ||||||
|  |     does not exceed 1.""" | ||||||
|  |     depths = set() | ||||||
|  |     populate_depths(tree_root, 0, depths) | ||||||
|  | 
 | ||||||
|  |     # cartesian product - only the top half | ||||||
|  |     for diff in set(abs(a - b) for a, b in combinations(depths, 2)): | ||||||
|  |         if diff > 1: | ||||||
|  |             return False | ||||||
|  | 
 | ||||||
|  |     return True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def populate_depths(node, depth, depths): | ||||||
|  |     if is_leaf(node): | ||||||
|  |         depths.add(depth) | ||||||
|  |     else: | ||||||
|  |         if node.left is not None: | ||||||
|  |             populate_depths(node.left, depth + 1, depths) | ||||||
|  |         if node.right is not None: | ||||||
|  |             populate_depths(node.right, depth + 1, depths) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     class BinaryTreeNode(object): | ||||||
|  |         def __init__(self, value): | ||||||
|  |             self.value = value | ||||||
|  |             self.left = None | ||||||
|  |             self.right = None | ||||||
|  | 
 | ||||||
|  |         def insert_left(self, value): | ||||||
|  |             self.left = Test.BinaryTreeNode(value) | ||||||
|  |             return self.left | ||||||
|  | 
 | ||||||
|  |         def insert_right(self, value): | ||||||
|  |             self.right = Test.BinaryTreeNode(value) | ||||||
|  |             return self.right | ||||||
|  | 
 | ||||||
|  |     def test_full_tree(self): | ||||||
|  |         tree = Test.BinaryTreeNode(5) | ||||||
|  |         left = tree.insert_left(8) | ||||||
|  |         right = tree.insert_right(6) | ||||||
|  |         left.insert_left(1) | ||||||
|  |         left.insert_right(2) | ||||||
|  |         right.insert_left(3) | ||||||
|  |         right.insert_right(4) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_both_leaves_at_the_same_depth(self): | ||||||
|  |         tree = Test.BinaryTreeNode(3) | ||||||
|  |         left = tree.insert_left(4) | ||||||
|  |         right = tree.insert_right(2) | ||||||
|  |         left.insert_left(1) | ||||||
|  |         right.insert_right(9) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_leaf_heights_differ_by_one(self): | ||||||
|  |         tree = Test.BinaryTreeNode(6) | ||||||
|  |         left = tree.insert_left(1) | ||||||
|  |         right = tree.insert_right(0) | ||||||
|  |         right.insert_right(7) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_leaf_heights_differ_by_two(self): | ||||||
|  |         tree = Test.BinaryTreeNode(6) | ||||||
|  |         left = tree.insert_left(1) | ||||||
|  |         right = tree.insert_right(0) | ||||||
|  |         right_right = right.insert_right(7) | ||||||
|  |         right_right.insert_right(8) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_three_leaves_total(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         left = tree.insert_left(5) | ||||||
|  |         right = tree.insert_right(9) | ||||||
|  |         right.insert_left(8) | ||||||
|  |         right.insert_right(5) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_both_subtrees_superbalanced(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         left = tree.insert_left(5) | ||||||
|  |         right = tree.insert_right(9) | ||||||
|  |         right_left = right.insert_left(8) | ||||||
|  |         right.insert_right(5) | ||||||
|  |         right_left.insert_left(7) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_both_subtrees_superbalanced_two(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         left = tree.insert_left(2) | ||||||
|  |         right = tree.insert_right(4) | ||||||
|  |         left.insert_left(3) | ||||||
|  |         left_right = left.insert_right(7) | ||||||
|  |         left_right.insert_right(8) | ||||||
|  |         right_right = right.insert_right(5) | ||||||
|  |         right_right_right = right_right.insert_right(6) | ||||||
|  |         right_right_right.insert_right(9) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_only_one_node(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_linked_list_tree(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         right = tree.insert_right(2) | ||||||
|  |         right_right = right.insert_right(3) | ||||||
|  |         right_right.insert_right(4) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										32
									
								
								universe/data_structures_and_algorithms/bit-manipulation.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								universe/data_structures_and_algorithms/bit-manipulation.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | def test(x, i): | ||||||
|  |     return x & (1 << i) != 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def set(x, i): | ||||||
|  |     return x | (1 << i) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def clear(x, i): | ||||||
|  |     return x & ~(1 << i) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def toggle(x, i): | ||||||
|  |     if test(x, i): | ||||||
|  |         return clear(x, i) | ||||||
|  |     else: | ||||||
|  |         return set(x, i) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_single(x): | ||||||
|  |     if x == 0: | ||||||
|  |         return False | ||||||
|  |     else: | ||||||
|  |         return x & (x - 1) == 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | print(test(0b1010, 3)) | ||||||
|  | print('{0:b}'.format(set(0b1010, 1))) | ||||||
|  | print('{0:b}'.format(clear(0b1010, 1))) | ||||||
|  | print('{0:b}'.format(toggle(0b1010, 2))) | ||||||
|  | print(test_single(0b1010)) | ||||||
|  | print(test_single(0b1000)) | ||||||
							
								
								
									
										63
									
								
								universe/data_structures_and_algorithms/bracket-validator.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								universe/data_structures_and_algorithms/bracket-validator.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | # is_valid :: String -> Boolean | ||||||
|  | def is_valid(xs): | ||||||
|  |     s = [] | ||||||
|  |     seeking = { | ||||||
|  |         '}': '{', | ||||||
|  |         ']': '[', | ||||||
|  |         ')': '(', | ||||||
|  |     } | ||||||
|  |     openers = seeking.values() | ||||||
|  |     closers = seeking.keys() | ||||||
|  |     for c in xs: | ||||||
|  |         if c in openers: | ||||||
|  |             s.append(c) | ||||||
|  |         elif c in closers: | ||||||
|  |             if not s: | ||||||
|  |                 return False | ||||||
|  |             elif s[-1] != seeking.get(c): | ||||||
|  |                 return False | ||||||
|  |             else: | ||||||
|  |                 s.pop() | ||||||
|  |     return len(s) == 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_valid_short_code(self): | ||||||
|  |         result = is_valid('()') | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_valid_longer_code(self): | ||||||
|  |         result = is_valid('([]{[]})[]{{}()}') | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_interleaved_openers_and_closers(self): | ||||||
|  |         result = is_valid('([)]') | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_mismatched_opener_and_closer(self): | ||||||
|  |         result = is_valid('([][]}') | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_missing_closer(self): | ||||||
|  |         result = is_valid('[[]()') | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_extra_closer(self): | ||||||
|  |         result = is_valid('[[]]())') | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_empty_string(self): | ||||||
|  |         result = is_valid('') | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										121
									
								
								universe/data_structures_and_algorithms/bst-checker.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								universe/data_structures_and_algorithms/bst-checker.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,121 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Implementation | ||||||
|  | ################################################################################ | ||||||
|  | # is_leaf :: Node(a) -> Boolean | ||||||
|  | def is_leaf(node): | ||||||
|  |     return not node.left and not node.right | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # is_binary_search_tree :: Node(Integer) -> Set(Int) -> Set(Int) -> Boolean | ||||||
|  | def is_binary_search_tree_a(node, la=set(), ra=set()): | ||||||
|  |     """My first solution for this problem.""" | ||||||
|  |     for x in la: | ||||||
|  |         if not node.value < x: | ||||||
|  |             return False | ||||||
|  |     for x in ra: | ||||||
|  |         if not node.value > x: | ||||||
|  |             return False | ||||||
|  |     if is_leaf(node): | ||||||
|  |         return True | ||||||
|  |     elif not node.left: | ||||||
|  |         return is_binary_search_tree( | ||||||
|  |             node.right, | ||||||
|  |             la=la, | ||||||
|  |             ra=ra ^ {node.value}, | ||||||
|  |         ) | ||||||
|  |     elif not node.right: | ||||||
|  |         return is_binary_search_tree(node.left, la=la ^ {node.value}, ra=ra) | ||||||
|  |     else: | ||||||
|  |         return all([ | ||||||
|  |             is_binary_search_tree(node.left, la=la ^ {node.value}, ra=ra), | ||||||
|  |             is_binary_search_tree(node.right, la=la, ra=ra ^ {node.value}) | ||||||
|  |         ]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # is_binary_search_tree :: Node(Int) -> Maybe(Int) -> Maybe(Int) -> Boolean | ||||||
|  | def is_binary_search_tree(node, lb=None, ub=None): | ||||||
|  |     if lb: | ||||||
|  |         if node.value < lb: | ||||||
|  |             return False | ||||||
|  |     if ub: | ||||||
|  |         if node.value > ub: | ||||||
|  |             return False | ||||||
|  |     if is_leaf(node): | ||||||
|  |         return True | ||||||
|  |     elif not node.right: | ||||||
|  |         return is_binary_search_tree(node.left, lb=lb, ub=node.value) | ||||||
|  |     elif not node.left: | ||||||
|  |         return is_binary_search_tree(node.right, lb=node.value, ub=ub) | ||||||
|  |     else: | ||||||
|  |         return is_binary_search_tree( | ||||||
|  |             node.left, lb=lb, ub=node.value) and is_binary_search_tree( | ||||||
|  |                 node.right, lb=node.value, ub=ub) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     class BinaryTreeNode(object): | ||||||
|  |         def __init__(self, value): | ||||||
|  |             self.value = value | ||||||
|  |             self.left = None | ||||||
|  |             self.right = None | ||||||
|  | 
 | ||||||
|  |         def insert_left(self, value): | ||||||
|  |             self.left = Test.BinaryTreeNode(value) | ||||||
|  |             return self.left | ||||||
|  | 
 | ||||||
|  |         def insert_right(self, value): | ||||||
|  |             self.right = Test.BinaryTreeNode(value) | ||||||
|  |             return self.right | ||||||
|  | 
 | ||||||
|  |     def test_valid_full_tree(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         left = tree.insert_left(30) | ||||||
|  |         right = tree.insert_right(70) | ||||||
|  |         left.insert_left(10) | ||||||
|  |         left.insert_right(40) | ||||||
|  |         right.insert_left(60) | ||||||
|  |         right.insert_right(80) | ||||||
|  |         result = is_binary_search_tree(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_both_subtrees_valid(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         left = tree.insert_left(30) | ||||||
|  |         right = tree.insert_right(80) | ||||||
|  |         left.insert_left(20) | ||||||
|  |         left.insert_right(60) | ||||||
|  |         right.insert_left(70) | ||||||
|  |         right.insert_right(90) | ||||||
|  |         result = is_binary_search_tree(tree) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_descending_linked_list(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         left = tree.insert_left(40) | ||||||
|  |         left_left = left.insert_left(30) | ||||||
|  |         left_left_left = left_left.insert_left(20) | ||||||
|  |         left_left_left.insert_left(10) | ||||||
|  |         result = is_binary_search_tree(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_out_of_order_linked_list(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         right = tree.insert_right(70) | ||||||
|  |         right_right = right.insert_right(60) | ||||||
|  |         right_right.insert_right(80) | ||||||
|  |         result = is_binary_search_tree(tree) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_one_node_tree(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         result = is_binary_search_tree(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,91 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Implementation | ||||||
|  | ################################################################################ | ||||||
|  | def is_first_come_first_served(to, di, xs): | ||||||
|  |     # All the guards, assertions we should need. | ||||||
|  |     if to == di == xs == []: | ||||||
|  |         return True | ||||||
|  |     elif to == di == []: | ||||||
|  |         return False | ||||||
|  |     elif to == []: | ||||||
|  |         return di == xs | ||||||
|  |     elif to == []: | ||||||
|  |         return di == xs | ||||||
|  |     elif di == []: | ||||||
|  |         return to == xs | ||||||
|  |     elif xs == []: | ||||||
|  |         return False | ||||||
|  |     elif len(xs) != (len(to) + len(di)): | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  |     fst, snd = to, di | ||||||
|  | 
 | ||||||
|  |     if xs[0] == to[0]: | ||||||
|  |         fst, snd = to, di | ||||||
|  |     elif xs[0] == di[0]: | ||||||
|  |         fst, snd = di, to | ||||||
|  |     else: | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  |     fst_done, snd_done = False, False | ||||||
|  |     fi, si = 1, 0 | ||||||
|  | 
 | ||||||
|  |     for i in range(1, len(xs)): | ||||||
|  |         # Short-circuit and avoid index-out-of-bounds without introducing overly | ||||||
|  |         # defensive, sloppy code. | ||||||
|  |         if fst_done: | ||||||
|  |             return snd[si:] == xs[i:] | ||||||
|  |         elif snd_done: | ||||||
|  |             return fst[fi:] == xs[i:] | ||||||
|  | 
 | ||||||
|  |         if fst[fi] == xs[i]: | ||||||
|  |             fi += 1 | ||||||
|  |         elif snd[si] == xs[i]: | ||||||
|  |             si += 1 | ||||||
|  |         else: | ||||||
|  |             return False | ||||||
|  | 
 | ||||||
|  |         fst_done, snd_done = fi == len(fst), si == len(snd) | ||||||
|  | 
 | ||||||
|  |     return True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_both_registers_have_same_number_of_orders(self): | ||||||
|  |         result = is_first_come_first_served([1, 4, 5], [2, 3, 6], | ||||||
|  |                                             [1, 2, 3, 4, 5, 6]) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_registers_have_different_lengths(self): | ||||||
|  |         result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 2, 6, 3, 5]) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_one_register_is_empty(self): | ||||||
|  |         result = is_first_come_first_served([], [2, 3, 6], [2, 3, 6]) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_served_orders_is_missing_orders(self): | ||||||
|  |         result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 6, 3, 5]) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_served_orders_has_extra_orders(self): | ||||||
|  |         result = is_first_come_first_served([1, 5], [2, 3, 6], | ||||||
|  |                                             [1, 2, 3, 5, 6, 8]) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_one_register_has_extra_orders(self): | ||||||
|  |         result = is_first_come_first_served([1, 9], [7, 8], [1, 7, 8]) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_one_register_has_unserved_orders(self): | ||||||
|  |         result = is_first_come_first_served([55, 9], [7, 8], [1, 7, 8, 9]) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										71
									
								
								universe/data_structures_and_algorithms/cake-thief.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								universe/data_structures_and_algorithms/cake-thief.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | ||||||
|  | import unittest | ||||||
|  | from math import floor | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def max_duffel_bag_value(xs, cap): | ||||||
|  |     ct = (cap + 1) | ||||||
|  |     maxes = [0] * ct | ||||||
|  |     for c in range(cap + 1): | ||||||
|  |         for w, v in xs: | ||||||
|  |             if w == 0 and v > 0: | ||||||
|  |                 return float('inf') | ||||||
|  |             if w == c: | ||||||
|  |                 maxes[c:] = [max(maxes[c], v)] * (ct - c) | ||||||
|  |             elif w < c: | ||||||
|  |                 d = c - w | ||||||
|  |                 maxes[c:] = [max(maxes[c], v + maxes[d])] * (ct - c) | ||||||
|  |             else: | ||||||
|  |                 continue | ||||||
|  |     return maxes[cap] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_one_cake(self): | ||||||
|  |         actual = max_duffel_bag_value([(2, 1)], 9) | ||||||
|  |         expected = 4 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_two_cakes(self): | ||||||
|  |         actual = max_duffel_bag_value([(4, 4), (5, 5)], 9) | ||||||
|  |         expected = 9 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_only_take_less_valuable_cake(self): | ||||||
|  |         actual = max_duffel_bag_value([(4, 4), (5, 5)], 12) | ||||||
|  |         expected = 12 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_lots_of_cakes(self): | ||||||
|  |         actual = max_duffel_bag_value([(2, 3), (3, 6), (5, 1), (6, 1), (7, 1), | ||||||
|  |                                        (8, 1)], 7) | ||||||
|  |         expected = 12 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_value_to_weight_ratio_is_not_optimal(self): | ||||||
|  |         actual = max_duffel_bag_value([(51, 52), (50, 50)], 100) | ||||||
|  |         expected = 100 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_zero_capacity(self): | ||||||
|  |         actual = max_duffel_bag_value([(1, 2)], 0) | ||||||
|  |         expected = 0 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_cake_with_zero_value_and_weight(self): | ||||||
|  |         actual = max_duffel_bag_value([(0, 0), (2, 1)], 7) | ||||||
|  |         expected = 3 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_cake_with_non_zero_value_and_zero_weight(self): | ||||||
|  |         actual = max_duffel_bag_value([(0, 5)], 5) | ||||||
|  |         expected = float('inf') | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										57
									
								
								universe/data_structures_and_algorithms/coins.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								universe/data_structures_and_algorithms/coins.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | import unittest | ||||||
|  | from math import floor | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | 
 | ||||||
|  | # change_possibilities :: Int -> [Int] -> Int | ||||||
|  | def change_possibilities(n, xs): | ||||||
|  |     combinations = [0] * (n + 1) | ||||||
|  |     combinations[0] = 1 | ||||||
|  | 
 | ||||||
|  |     for x in xs: | ||||||
|  |         for i in range(len(combinations)): | ||||||
|  |             if i >= x: | ||||||
|  |                 combinations[i] += combinations[i - x] | ||||||
|  | 
 | ||||||
|  |     return combinations[n] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  | 
 | ||||||
|  |     def test_sample_input(self): | ||||||
|  |         actual = change_possibilities(4, (1, 2, 3)) | ||||||
|  |         expected = 4 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_way_to_make_zero_cents(self): | ||||||
|  |         actual = change_possibilities(0, (1, 2)) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_no_ways_if_no_coins(self): | ||||||
|  |         actual = change_possibilities(1, ()) | ||||||
|  |         expected = 0 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_big_coin_value(self): | ||||||
|  |         actual = change_possibilities(5, (25, 50)) | ||||||
|  |         expected = 0 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_big_target_amount(self): | ||||||
|  |         actual = change_possibilities(50, (5, 10)) | ||||||
|  |         expected = 6 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_change_for_one_dollar(self): | ||||||
|  |         actual = change_possibilities(100, (1, 5, 10, 25, 50)) | ||||||
|  |         expected = 292 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | from itertools import product | ||||||
|  | from random import choice | ||||||
|  | from time import sleep | ||||||
|  | from os import system | ||||||
|  | from math import floor | ||||||
|  | from colorama import Back, Fore, Style | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Simulation of Conway's Game of Life. The goal here was to write this with a | ||||||
|  | # small amount of code as a proof-of-concept that could be run in the terminal. | ||||||
|  | # | ||||||
|  | # If you'd like to tinker with the rules, see the conditionals defined in the | ||||||
|  | # `advance/1` function. For other parameters, like the board size and refresh | ||||||
|  | # rate, refer to the while-loop defined at the bottom of this file. | ||||||
|  | ################################################################################ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def init_board(n, init_alive_percentage): | ||||||
|  |     """Initialize a board of size `n` by `n`. Supply a percentage, | ||||||
|  |     `init_alive_percentage`, representing the number of cells in the board that | ||||||
|  |     should be alive from the start.""" | ||||||
|  |     alive_count = floor(n * init_alive_percentage) | ||||||
|  |     distribution = [True] * alive_count + [False] * (n - alive_count) | ||||||
|  |     return [[choice(distribution) for _ in range(n)] for _ in range(n)] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def neighbors(coord, board): | ||||||
|  |     """Return the neighbors for a given `coord` on a `board`.""" | ||||||
|  |     n = len(board) | ||||||
|  |     row, col = coord | ||||||
|  |     return [ | ||||||
|  |         board[(row + row_d) % n][(col + col_d) % n] | ||||||
|  |         for row_d, col_d in product([-1, 0, 1], [-1, 0, 1]) | ||||||
|  |         if (row_d, col_d) != (0, 0) | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def advance(board): | ||||||
|  |     """Advance the state of the `board` from T[n] to T[n+1].""" | ||||||
|  |     n = len(board) | ||||||
|  |     new_board = [[False for _ in range(n)] for _ in range(n)] | ||||||
|  |     for row in range(n): | ||||||
|  |         for col in range(n): | ||||||
|  |             alive_count = len([x for x in neighbors((row, col), board) if x]) | ||||||
|  |             # Loneliness | ||||||
|  |             if alive_count == 0: | ||||||
|  |                 new_board[row][col] = False | ||||||
|  |             # Status Quo | ||||||
|  |             elif alive_count == 1: | ||||||
|  |                 new_board[row][col] = board[row][col] | ||||||
|  |             # Cooperation | ||||||
|  |             elif alive_count == 2: | ||||||
|  |                 new_board[row][col] = True | ||||||
|  |             # Resource starvation | ||||||
|  |             elif alive_count >= 3: | ||||||
|  |                 new_board[row][col] = False | ||||||
|  |     return new_board | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def print_board(board): | ||||||
|  |     """Print the game `board` in a human-readable way.""" | ||||||
|  |     result = '' | ||||||
|  |     for row in board: | ||||||
|  |         for col in row: | ||||||
|  |             if col: | ||||||
|  |                 result += Back.GREEN + '1 ' + Style.RESET_ALL | ||||||
|  |             else: | ||||||
|  |                 result += Back.RED + '0 ' + Style.RESET_ALL | ||||||
|  |         result += '\n' | ||||||
|  |     print(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | board = init_board(100, 0.50) | ||||||
|  | while True: | ||||||
|  |     system('clear') | ||||||
|  |     print_board(board) | ||||||
|  |     sleep(0.15) | ||||||
|  |     board = advance(board) | ||||||
							
								
								
									
										60
									
								
								universe/data_structures_and_algorithms/delete-node.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								universe/data_structures_and_algorithms/delete-node.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def delete_node(x): | ||||||
|  |     if not x.next: | ||||||
|  |         raise Exception('Cannot delete the last node in a linked list.') | ||||||
|  |     else: | ||||||
|  |         x.value = x.next.value | ||||||
|  |         x.next = x.next.next | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     class LinkedListNode(object): | ||||||
|  |         def __init__(self, value, next=None): | ||||||
|  |             self.value = value | ||||||
|  |             self.next = next | ||||||
|  | 
 | ||||||
|  |         def get_values(self): | ||||||
|  |             node = self | ||||||
|  |             values = [] | ||||||
|  |             while node is not None: | ||||||
|  |                 values.append(node.value) | ||||||
|  |                 node = node.next | ||||||
|  |             return values | ||||||
|  | 
 | ||||||
|  |     def setUp(self): | ||||||
|  |         self.fourth = Test.LinkedListNode(4) | ||||||
|  |         self.third = Test.LinkedListNode(3, self.fourth) | ||||||
|  |         self.second = Test.LinkedListNode(2, self.third) | ||||||
|  |         self.first = Test.LinkedListNode(1, self.second) | ||||||
|  | 
 | ||||||
|  |     def test_node_at_beginning(self): | ||||||
|  |         delete_node(self.first) | ||||||
|  |         actual = self.first.get_values() | ||||||
|  |         expected = [2, 3, 4] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_node_in_middle(self): | ||||||
|  |         delete_node(self.second) | ||||||
|  |         actual = self.first.get_values() | ||||||
|  |         expected = [1, 3, 4] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_node_at_end(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             delete_node(self.fourth) | ||||||
|  | 
 | ||||||
|  |     def test_one_node_in_list(self): | ||||||
|  |         unique = Test.LinkedListNode(1) | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             delete_node(unique) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										65
									
								
								universe/data_structures_and_algorithms/dft.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								universe/data_structures_and_algorithms/dft.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | ||||||
|  | from random import choice | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Node(object): | ||||||
|  |     def __init__(self, value=None, left=None, right=None): | ||||||
|  |         self.value = value | ||||||
|  |         self.left = left | ||||||
|  |         self.right = left | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def p(node, indent=0): | ||||||
|  |     print(indent * ' ' + '|-' + str(node.value)) | ||||||
|  |     if node.left is not None: | ||||||
|  |         p(node.left, indent=indent + 2) | ||||||
|  |     if node.right is not None: | ||||||
|  |         p(node.right, indent=indent + 2) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # read trees (i.e. traversing, parsing) | ||||||
|  | # write trees (i.e. generating, printing) | ||||||
|  | def random(d=0): | ||||||
|  |     left = None | ||||||
|  |     right = None | ||||||
|  | 
 | ||||||
|  |     if choice([True, False]): | ||||||
|  |         left = random(d + 1) | ||||||
|  | 
 | ||||||
|  |     if choice([True, False]): | ||||||
|  |         right = random(d + 1) | ||||||
|  | 
 | ||||||
|  |     return Node( | ||||||
|  |         value=d, | ||||||
|  |         left=left, | ||||||
|  |         right=right, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # DFTs can be: | ||||||
|  | # - imperative (mutable) | ||||||
|  | # - functional (immutable) | ||||||
|  | # - iterative | ||||||
|  | # - recursive | ||||||
|  | ################################################################################ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Iterative | ||||||
|  | def traverse(node, f): | ||||||
|  |     stack = [(node, 0)] | ||||||
|  | 
 | ||||||
|  |     while len(stack): | ||||||
|  |         node, depth = stack.pop() | ||||||
|  |         f(node, depth) | ||||||
|  |         print(depth) | ||||||
|  | 
 | ||||||
|  |         if node.left is not None: | ||||||
|  |             stack.append((node.left, depth + 1)) | ||||||
|  |         if node.right is not None: | ||||||
|  |             stack.append((node.right, depth + 1)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | print('----------------------------------------------------------------------') | ||||||
|  | for _ in range(10): | ||||||
|  |     traverse(random(), lambda _, d: print(d)) | ||||||
|  | print() | ||||||
|  | @ -0,0 +1,48 @@ | ||||||
|  | from collections import deque | ||||||
|  | from heapq import heappush, heappop | ||||||
|  | from fixtures import weighted_graph | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def put(t, x, xs): | ||||||
|  |     if t == 'stack': | ||||||
|  |         return xs.append(x) | ||||||
|  |     if t == 'queue': | ||||||
|  |         return xs.append(x) | ||||||
|  |     if t == 'priority': | ||||||
|  |         return heappush(xs, x) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def pop(t, xs): | ||||||
|  |     if t == 'stack': | ||||||
|  |         return xs.pop() | ||||||
|  |     if t == 'queue': | ||||||
|  |         return xs.popleft() | ||||||
|  |     if t == 'priority': | ||||||
|  |         return heappop(xs) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # shortest_path :: Vertex -> Vertex -> Graph -> [Vertex] | ||||||
|  | def shortest_path(a, b, g): | ||||||
|  |     """Returns the shortest path from vertex a to vertex b in graph g.""" | ||||||
|  |     t = 'priority' | ||||||
|  |     xs = [] | ||||||
|  |     seen = set() | ||||||
|  |     # Map(Weight, [Vertex]) | ||||||
|  |     m = {} | ||||||
|  | 
 | ||||||
|  |     put(t, (0, [a], a), xs) | ||||||
|  | 
 | ||||||
|  |     while xs: | ||||||
|  |         w0, path, v = pop(t, xs) | ||||||
|  | 
 | ||||||
|  |         seen.add(v) | ||||||
|  |         if v == b: | ||||||
|  |             m[w0] = path | ||||||
|  |         for w1, x in g.get(v): | ||||||
|  |             if x not in seen: | ||||||
|  |                 put(t, (w0 + w1, path + [x], x), xs) | ||||||
|  | 
 | ||||||
|  |     return m | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | print(shortest_path('a', 'f', graph_a)) | ||||||
|  | @ -0,0 +1,56 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def find_duplicate(xs): | ||||||
|  |     self_ref_count = 0 | ||||||
|  |     for i in range(len(xs)): | ||||||
|  |         if xs[i] == i + 1: | ||||||
|  |             self_ref_count += 1 | ||||||
|  |     hops = len(xs) - 1 - self_ref_count | ||||||
|  |     current = xs[-1] | ||||||
|  |     while hops > 0: | ||||||
|  |         current = xs[current - 1] | ||||||
|  |         hops -= 1 | ||||||
|  |     return current | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     # TODO: Debug why this fails. | ||||||
|  |     def test_darren_from_interview_cake(self): | ||||||
|  |         actual = find_duplicate([4, 1, 8, 3, 2, 7, 6, 5, 4]) | ||||||
|  |         expected = 4 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_just_the_repeated_number(self): | ||||||
|  |         actual = find_duplicate([1, 1]) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_short_list(self): | ||||||
|  |         actual = find_duplicate([1, 2, 3, 2]) | ||||||
|  |         expected = 2 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_last_cycle(self): | ||||||
|  |         actual = find_duplicate([3, 4, 2, 3, 1, 5]) | ||||||
|  |         expected = 3 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_medium_list(self): | ||||||
|  |         actual = find_duplicate([1, 2, 5, 5, 5, 5]) | ||||||
|  |         expected = 5 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_long_list(self): | ||||||
|  |         actual = find_duplicate([4, 1, 4, 8, 3, 2, 7, 6, 5]) | ||||||
|  |         expected = 4 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,61 @@ | ||||||
|  | from math import floor | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def bounds(r): | ||||||
|  |     ct = len(r) | ||||||
|  |     if ct % 2 == 0: | ||||||
|  |         h = int(ct / 2) | ||||||
|  |         return ct, h | ||||||
|  |     else: | ||||||
|  |         h = floor(ct / 2) | ||||||
|  |         return ct, h | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def find_repeat(xs): | ||||||
|  |     ct, h = bounds(xs) | ||||||
|  |     rl = range(1, h + 1) | ||||||
|  |     rr = range(h + 1, ct) | ||||||
|  |     while True: | ||||||
|  |         nl = len([None for x in xs if x in rl]) | ||||||
|  |         nr = len([None for x in xs if x in rr]) | ||||||
|  |         branch = rl if nl > nr else rr | ||||||
|  |         if len(branch) == 1: | ||||||
|  |             return branch[0] | ||||||
|  |         ct, h = bounds(branch) | ||||||
|  |         rl = range(branch[0], branch[0]) | ||||||
|  |         rr = range(branch[0] + h, branch[-1] + 1) | ||||||
|  |     raise Exception( | ||||||
|  |         'We could not find any duplicates in xs. Perhaps xs did not adhere to the usage contract.' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_just_the_repeated_number(self): | ||||||
|  |         actual = find_repeat([1, 1]) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_short_list(self): | ||||||
|  |         actual = find_repeat([1, 2, 3, 2]) | ||||||
|  |         expected = 2 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_medium_list(self): | ||||||
|  |         actual = find_repeat([1, 2, 5, 5, 5, 5]) | ||||||
|  |         expected = 5 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_long_list(self): | ||||||
|  |         actual = find_repeat([4, 1, 4, 8, 3, 2, 7, 6, 5]) | ||||||
|  |         expected = 4 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def find_rotation_point(xs): | ||||||
|  |     """Usage of `visited` here is a hack, but works for the test cases | ||||||
|  |     (gulp).""" | ||||||
|  |     i = 0 | ||||||
|  |     j = round(len(xs) / 2) | ||||||
|  |     result = None | ||||||
|  |     visited = set() | ||||||
|  |     while not result: | ||||||
|  |         if i in visited: | ||||||
|  |             i += 1 | ||||||
|  |         if j in visited: | ||||||
|  |             j -= 1 | ||||||
|  |         visited.add(i) | ||||||
|  |         visited.add(j) | ||||||
|  |         if xs[j - 1] > xs[j]: | ||||||
|  |             result = j | ||||||
|  |         elif xs[i] < xs[j]: | ||||||
|  |             i = j | ||||||
|  |             j += round((len(xs) - j) / 2) | ||||||
|  |         elif xs[i] >= xs[j]: | ||||||
|  |             i = j | ||||||
|  |             j -= round((j - i) / 2) | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_small_list(self): | ||||||
|  |         actual = find_rotation_point(['cape', 'cake']) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_medium_list(self): | ||||||
|  |         actual = find_rotation_point( | ||||||
|  |             ['grape', 'orange', 'plum', 'radish', 'apple']) | ||||||
|  |         expected = 4 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_large_list(self): | ||||||
|  |         actual = find_rotation_point([ | ||||||
|  |             'ptolemaic', 'retrograde', 'supplant', 'undulate', 'xenoepist', | ||||||
|  |             'asymptote', 'babka', 'banoffee', 'engender', 'karpatka', | ||||||
|  |             'othellolagkage' | ||||||
|  |         ]) | ||||||
|  |         expected = 5 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     # Are we missing any edge cases? | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,45 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def find_unique_delivery_id(xs): | ||||||
|  |     a = 0 | ||||||
|  |     for x in xs: | ||||||
|  |         a ^= x | ||||||
|  |     return a | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_one_drone(self): | ||||||
|  |         actual = find_unique_delivery_id([1]) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_unique_id_comes_first(self): | ||||||
|  |         actual = find_unique_delivery_id([1, 2, 2]) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_unique_id_comes_last(self): | ||||||
|  |         actual = find_unique_delivery_id([3, 3, 2, 2, 1]) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_unique_id_in_middle(self): | ||||||
|  |         actual = find_unique_delivery_id([3, 2, 1, 2, 3]) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_many_drones(self): | ||||||
|  |         actual = find_unique_delivery_id( | ||||||
|  |             [2, 5, 4, 8, 6, 3, 1, 4, 2, 3, 6, 5, 1]) | ||||||
|  |         expected = 8 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										110
									
								
								universe/data_structures_and_algorithms/fixtures.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								universe/data_structures_and_algorithms/fixtures.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | ||||||
|  | # Using this module to store commonly used, but annoying to create, data | ||||||
|  | # structures for my test inputs. | ||||||
|  | # | ||||||
|  | # Use like: | ||||||
|  | # from fixtures import graph_a | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Constants | ||||||
|  | ################################################################################ | ||||||
|  | 
 | ||||||
|  | edge_list = [ | ||||||
|  |     ('a', 'b'), | ||||||
|  |     ('a', 'c'), | ||||||
|  |     ('a', 'e'), | ||||||
|  |     ('b', 'c'), | ||||||
|  |     ('b', 'd'), | ||||||
|  |     ('c', 'e'), | ||||||
|  |     ('d', 'f'), | ||||||
|  |     ('e', 'd'), | ||||||
|  |     ('e', 'f'), | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | unweighted_graph = { | ||||||
|  |     'a': {'b', 'c', 'e'}, | ||||||
|  |     'b': {'c', 'd'}, | ||||||
|  |     'c': {'e'}, | ||||||
|  |     'd': {'f'}, | ||||||
|  |     'e': {'d', 'f'}, | ||||||
|  |     'f': set(), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | adjacencies = { | ||||||
|  |     'a': { | ||||||
|  |         'a': False, | ||||||
|  |         'b': False | ||||||
|  |     }, | ||||||
|  |     'a': [], | ||||||
|  |     'a': [], | ||||||
|  |     'a': [], | ||||||
|  |     'a': [], | ||||||
|  |     'a': [], | ||||||
|  |     'a': [], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | weighted_graph = { | ||||||
|  |     'a': {(4, 'b'), (2, 'c'), (4, 'e')}, | ||||||
|  |     'b': {(5, 'c'), (10, 'd')}, | ||||||
|  |     'c': {(3, 'e')}, | ||||||
|  |     'd': {(11, 'f')}, | ||||||
|  |     'e': {(4, 'd'), (5, 'f')}, | ||||||
|  |     'f': set(), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # This is `weighted_graph` with each of its weighted edges "expanded". | ||||||
|  | expanded_weights_graph = { | ||||||
|  |     'a': ['b-1', 'c-1', 'e-1'], | ||||||
|  |     'b-1': ['b-2'], | ||||||
|  |     'b-2': ['b-3'], | ||||||
|  |     'b-3': ['b'], | ||||||
|  |     'c-1': ['c'], | ||||||
|  |     'e-1': ['e-2'], | ||||||
|  |     'e-2': ['e-3'], | ||||||
|  |     'e-3': ['e'], | ||||||
|  |     # and so on... | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | unweighted_digraph = { | ||||||
|  |     '5': {'2', '0'}, | ||||||
|  |     '4': {'0', '1'}, | ||||||
|  |     '3': {'1'}, | ||||||
|  |     '2': {'3'}, | ||||||
|  |     '1': set(), | ||||||
|  |     '0': set(), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Functions | ||||||
|  | ################################################################################ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def vertices(xs): | ||||||
|  |     result = set() | ||||||
|  |     for a, b in xs: | ||||||
|  |         result.add(a) | ||||||
|  |         result.add(b) | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def edges_to_neighbors(xs): | ||||||
|  |     result = {v: set() for v in vertices(xs)} | ||||||
|  |     for a, b in xs: | ||||||
|  |         result[a].add(b) | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def neighbors_to_edges(xs): | ||||||
|  |     result = [] | ||||||
|  |     for k, ys in xs.items(): | ||||||
|  |         for y in ys: | ||||||
|  |             result.append((k, y)) | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def edges_to_adjacencies(xs): | ||||||
|  |     return xs | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Skipping handling adjacencies because I cannot think of a reasonable use-case | ||||||
|  | # for it when the vertex labels are items other than integers. I can think of | ||||||
|  | # ways of handling this, but none excite me. | ||||||
							
								
								
									
										180
									
								
								universe/data_structures_and_algorithms/graph-coloring.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								universe/data_structures_and_algorithms/graph-coloring.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,180 @@ | ||||||
|  | import unittest | ||||||
|  | from collections import deque | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | class GraphNode: | ||||||
|  |     def __init__(self, label): | ||||||
|  |         self.label = label | ||||||
|  |         self.neighbors = set() | ||||||
|  |         self.color = None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # color_graph :: G(V, E) -> Set(Color) -> IO () | ||||||
|  | def color_graph(graph, colors): | ||||||
|  |     q = deque() | ||||||
|  |     seen = set() | ||||||
|  |     q.append(graph[0]) | ||||||
|  | 
 | ||||||
|  |     while q: | ||||||
|  |         node = q.popleft() | ||||||
|  | 
 | ||||||
|  |         illegal = {n.color for n in node.neighbors} | ||||||
|  |         for x in colors: | ||||||
|  |             if x not in illegal: | ||||||
|  |                 node.color = x | ||||||
|  | 
 | ||||||
|  |         seen.add(node) | ||||||
|  | 
 | ||||||
|  |         for x in node.neighbors: | ||||||
|  |             if x not in seen: | ||||||
|  |                 q.append(x) | ||||||
|  | 
 | ||||||
|  |         # TODO: Is this the best way to traverse separate graphs? | ||||||
|  |         for x in graph: | ||||||
|  |             if x not in seen: | ||||||
|  |                 q.append(x) | ||||||
|  | 
 | ||||||
|  |     return 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def setUp(self): | ||||||
|  |         self.colors = frozenset([ | ||||||
|  |             'red', | ||||||
|  |             'green', | ||||||
|  |             'blue', | ||||||
|  |             'orange', | ||||||
|  |             'yellow', | ||||||
|  |             'white', | ||||||
|  |         ]) | ||||||
|  | 
 | ||||||
|  |     def assertGraphColoring(self, graph, colors): | ||||||
|  |         self.assertGraphHasColors(graph, colors) | ||||||
|  |         self.assertGraphColorLimit(graph) | ||||||
|  |         for node in graph: | ||||||
|  |             self.assertNodeUniqueColor(node) | ||||||
|  | 
 | ||||||
|  |     def assertGraphHasColors(self, graph, colors): | ||||||
|  |         for node in graph: | ||||||
|  |             msg = 'Node %r color %r not in %r' % (node.label, node.color, | ||||||
|  |                                                   colors) | ||||||
|  |             self.assertIn(node.color, colors, msg=msg) | ||||||
|  | 
 | ||||||
|  |     def assertGraphColorLimit(self, graph): | ||||||
|  |         max_degree = 0 | ||||||
|  |         colors_found = set() | ||||||
|  |         for node in graph: | ||||||
|  |             degree = len(node.neighbors) | ||||||
|  |             max_degree = max(degree, max_degree) | ||||||
|  |             colors_found.add(node.color) | ||||||
|  |         max_colors = max_degree + 1 | ||||||
|  |         used_colors = len(colors_found) | ||||||
|  |         msg = 'Used %d colors and expected %d at most' % (used_colors, | ||||||
|  |                                                           max_colors) | ||||||
|  |         self.assertLessEqual(used_colors, max_colors, msg=msg) | ||||||
|  | 
 | ||||||
|  |     def assertNodeUniqueColor(self, node): | ||||||
|  |         for adjacent in node.neighbors: | ||||||
|  |             msg = 'Adjacent nodes %r and %r have the same color %r' % ( | ||||||
|  |                 node.label, | ||||||
|  |                 adjacent.label, | ||||||
|  |                 node.color, | ||||||
|  |             ) | ||||||
|  |             self.assertNotEqual(node.color, adjacent.color, msg=msg) | ||||||
|  | 
 | ||||||
|  |     def test_line_graph(self): | ||||||
|  |         node_a = GraphNode('a') | ||||||
|  |         node_b = GraphNode('b') | ||||||
|  |         node_c = GraphNode('c') | ||||||
|  |         node_d = GraphNode('d') | ||||||
|  | 
 | ||||||
|  |         node_a.neighbors.add(node_b) | ||||||
|  |         node_b.neighbors.add(node_a) | ||||||
|  |         node_b.neighbors.add(node_c) | ||||||
|  |         node_c.neighbors.add(node_b) | ||||||
|  |         node_c.neighbors.add(node_d) | ||||||
|  |         node_d.neighbors.add(node_c) | ||||||
|  | 
 | ||||||
|  |         graph = [node_a, node_b, node_c, node_d] | ||||||
|  |         tampered_colors = list(self.colors) | ||||||
|  |         color_graph(graph, tampered_colors) | ||||||
|  |         self.assertGraphColoring(graph, self.colors) | ||||||
|  | 
 | ||||||
|  |     def test_separate_graph(self): | ||||||
|  |         node_a = GraphNode('a') | ||||||
|  |         node_b = GraphNode('b') | ||||||
|  |         node_c = GraphNode('c') | ||||||
|  |         node_d = GraphNode('d') | ||||||
|  | 
 | ||||||
|  |         node_a.neighbors.add(node_b) | ||||||
|  |         node_b.neighbors.add(node_a) | ||||||
|  |         node_c.neighbors.add(node_d) | ||||||
|  |         node_d.neighbors.add(node_c) | ||||||
|  | 
 | ||||||
|  |         graph = [node_a, node_b, node_c, node_d] | ||||||
|  |         tampered_colors = list(self.colors) | ||||||
|  |         color_graph(graph, tampered_colors) | ||||||
|  |         self.assertGraphColoring(graph, self.colors) | ||||||
|  | 
 | ||||||
|  |     def test_triangle_graph(self): | ||||||
|  |         node_a = GraphNode('a') | ||||||
|  |         node_b = GraphNode('b') | ||||||
|  |         node_c = GraphNode('c') | ||||||
|  | 
 | ||||||
|  |         node_a.neighbors.add(node_b) | ||||||
|  |         node_a.neighbors.add(node_c) | ||||||
|  |         node_b.neighbors.add(node_a) | ||||||
|  |         node_b.neighbors.add(node_c) | ||||||
|  |         node_c.neighbors.add(node_a) | ||||||
|  |         node_c.neighbors.add(node_b) | ||||||
|  | 
 | ||||||
|  |         graph = [node_a, node_b, node_c] | ||||||
|  |         tampered_colors = list(self.colors) | ||||||
|  |         color_graph(graph, tampered_colors) | ||||||
|  |         self.assertGraphColoring(graph, self.colors) | ||||||
|  | 
 | ||||||
|  |     def test_envelope_graph(self): | ||||||
|  |         node_a = GraphNode('a') | ||||||
|  |         node_b = GraphNode('b') | ||||||
|  |         node_c = GraphNode('c') | ||||||
|  |         node_d = GraphNode('d') | ||||||
|  |         node_e = GraphNode('e') | ||||||
|  | 
 | ||||||
|  |         node_a.neighbors.add(node_b) | ||||||
|  |         node_a.neighbors.add(node_c) | ||||||
|  |         node_b.neighbors.add(node_a) | ||||||
|  |         node_b.neighbors.add(node_c) | ||||||
|  |         node_b.neighbors.add(node_d) | ||||||
|  |         node_b.neighbors.add(node_e) | ||||||
|  |         node_c.neighbors.add(node_a) | ||||||
|  |         node_c.neighbors.add(node_b) | ||||||
|  |         node_c.neighbors.add(node_d) | ||||||
|  |         node_c.neighbors.add(node_e) | ||||||
|  |         node_d.neighbors.add(node_b) | ||||||
|  |         node_d.neighbors.add(node_c) | ||||||
|  |         node_d.neighbors.add(node_e) | ||||||
|  |         node_e.neighbors.add(node_b) | ||||||
|  |         node_e.neighbors.add(node_c) | ||||||
|  |         node_e.neighbors.add(node_d) | ||||||
|  | 
 | ||||||
|  |         graph = [node_a, node_b, node_c, node_d, node_e] | ||||||
|  |         tampered_colors = list(self.colors) | ||||||
|  |         color_graph(graph, tampered_colors) | ||||||
|  |         self.assertGraphColoring(graph, self.colors) | ||||||
|  | 
 | ||||||
|  |     def test_loop_graph(self): | ||||||
|  |         node_a = GraphNode('a') | ||||||
|  |         node_a.neighbors.add(node_a) | ||||||
|  |         graph = [node_a] | ||||||
|  |         tampered_colors = list(self.colors) | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             color_graph(graph, tampered_colors) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										39
									
								
								universe/data_structures_and_algorithms/graph-to-graphviz.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								universe/data_structures_and_algorithms/graph-to-graphviz.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | ||||||
|  | from graphviz import Digraph | ||||||
|  | from collections import deque | ||||||
|  | from fixtures import weighted_graph | ||||||
|  | 
 | ||||||
|  | # There are three ways to model a graph: | ||||||
|  | # 1. Edge list: [(Vertex, Vertex)] | ||||||
|  | # 2. Neighbors table: Map(Vertex, [Vertex]) | ||||||
|  | # 3. Adjacency matrix: [[Boolean]] | ||||||
|  | # | ||||||
|  | # The following graph is a neighbors table. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # to_graphviz :: Vertex -> Map(Vertex, [(Vertex, Weight)]) -> String | ||||||
|  | def to_graphviz(start, g): | ||||||
|  |     """Compiles the graph into GraphViz.""" | ||||||
|  |     d = Digraph() | ||||||
|  |     q = deque() | ||||||
|  |     seen = set() | ||||||
|  | 
 | ||||||
|  |     q.append(start) | ||||||
|  | 
 | ||||||
|  |     while q: | ||||||
|  |         v = q.popleft() | ||||||
|  |         if v in seen: | ||||||
|  |             continue | ||||||
|  |         d.node(v, label=v) | ||||||
|  | 
 | ||||||
|  |         for w, x in g[v]: | ||||||
|  |             d.edge(v, x, label=str(w)) | ||||||
|  |             q.append(x) | ||||||
|  |         seen.add(v) | ||||||
|  | 
 | ||||||
|  |     return d.source | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | with open('/tmp/test.gv', 'w') as f: | ||||||
|  |     src = to_graphviz('a', weighted_graph) | ||||||
|  |     f.write(src) | ||||||
|  |     print('/tmp/test.gv created!') | ||||||
|  | @ -0,0 +1,89 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | # f :: [Int] -> Int | ||||||
|  | def highest_product_of_3(xs): | ||||||
|  |     """Here we're greedily storing: | ||||||
|  |     - current max | ||||||
|  |     - largest product of two | ||||||
|  |     - largest positive number | ||||||
|  |     - second largest positive number | ||||||
|  |     - largest negative number | ||||||
|  |     """ | ||||||
|  |     if len(xs) < 3: | ||||||
|  |         raise Exception | ||||||
|  | 
 | ||||||
|  |     cm = None | ||||||
|  |     ld = xs[0] * xs[1] | ||||||
|  |     l2 = min(xs[0], xs[1]) | ||||||
|  |     if xs[0] < 0 or xs[1] < 0: | ||||||
|  |         ln = min(xs[0], xs[1]) | ||||||
|  |     else: | ||||||
|  |         ln = 1 | ||||||
|  |     l = max(xs[0], xs[1]) | ||||||
|  | 
 | ||||||
|  |     for x in xs[2:]: | ||||||
|  |         if not cm: | ||||||
|  |             cm = max(x * ln * l, ld * x, x * l * l2)  # beware | ||||||
|  |             ld = max(ld, x * ln, x * l) | ||||||
|  |             ln = min(ln, x) | ||||||
|  |             l = max(l, x) | ||||||
|  |             if x < l: | ||||||
|  |                 l2 = max(l2, x) | ||||||
|  |         else: | ||||||
|  |             cm = max(cm, x * ln * l, x * ld, x * l * l2) | ||||||
|  |             ld = max(ld, x * ln, x * l) | ||||||
|  |             ln = min(ln, x) | ||||||
|  |             l = max(l, x) | ||||||
|  |             if x < l: | ||||||
|  |                 l2 = max(l2, x) | ||||||
|  | 
 | ||||||
|  |     return cm | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_short_list(self): | ||||||
|  |         actual = highest_product_of_3([1, 2, 3, 4]) | ||||||
|  |         expected = 24 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_longer_list(self): | ||||||
|  |         actual = highest_product_of_3([6, 1, 3, 5, 7, 8, 2]) | ||||||
|  |         expected = 336 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_list_has_one_negative(self): | ||||||
|  |         actual = highest_product_of_3([-5, 4, 8, 2, 3]) | ||||||
|  |         expected = 96 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_list_has_two_negatives(self): | ||||||
|  |         actual = highest_product_of_3([-10, 1, 3, 2, -10]) | ||||||
|  |         expected = 300 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_list_is_all_negatives(self): | ||||||
|  |         actual = highest_product_of_3([-5, -1, -3, -2]) | ||||||
|  |         expected = -6 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_error_with_empty_list(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             highest_product_of_3([]) | ||||||
|  | 
 | ||||||
|  |     def test_error_with_one_number(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             highest_product_of_3([1]) | ||||||
|  | 
 | ||||||
|  |     def test_error_with_two_numbers(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             highest_product_of_3([1, 1]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,35 @@ | ||||||
|  | # possible :: Int -> [Int] -> Bool | ||||||
|  | def possible(flight_duration, film_durations): | ||||||
|  |     seeking = set() | ||||||
|  | 
 | ||||||
|  |     for x in film_durations: | ||||||
|  |         if x in seeking: | ||||||
|  |             return True | ||||||
|  |         else: | ||||||
|  |             seeking.add(flight_duration - x) | ||||||
|  | 
 | ||||||
|  |     return False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | should = [ | ||||||
|  |     (10, [1, 9, 8, 8, 8]), | ||||||
|  |     (10, [1, 9]), | ||||||
|  |     (10, [1, 9, 5, 5, 6]), | ||||||
|  |     (1, [0.5, 0.5]), | ||||||
|  |     (1, [0.5, 0.5]), | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | for a, b in should: | ||||||
|  |     print("Testing: %s %s" % (a, b)) | ||||||
|  |     assert possible(a, b) | ||||||
|  | 
 | ||||||
|  | shouldnt = [ | ||||||
|  |     (10, [1, 10, 1, 2, 1, 12]), | ||||||
|  |     (1, [0.25, 0.25, 0.25, 0.25]), | ||||||
|  |     (5, [1, 2, 2]), | ||||||
|  | ] | ||||||
|  | for a, b in shouldnt: | ||||||
|  |     print("Testing: %s %s" % (a, b)) | ||||||
|  |     assert not possible(a, b) | ||||||
|  | 
 | ||||||
|  | print("Tests pass") | ||||||
							
								
								
									
										38
									
								
								universe/data_structures_and_algorithms/knapsack-0-1.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								universe/data_structures_and_algorithms/knapsack-0-1.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | ||||||
|  | import unittest | ||||||
|  | from math import floor | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def knapify(xs, capacity=None): | ||||||
|  |     assert capacity is not None | ||||||
|  |     n = len(xs) | ||||||
|  |     # For 0/1 Knapsack, we must use a table, since this will encode which values | ||||||
|  |     # work for which items. This is cleaner than including a separate data | ||||||
|  |     # structure to capture it. | ||||||
|  |     maxes = [[0 for x in range(capacity + 1)] for x in range(n + 1)] | ||||||
|  | 
 | ||||||
|  |     # Build table maxes[][] in bottom up manner | ||||||
|  |     for row in range(n + 1): | ||||||
|  |         for col in range(capacity + 1): | ||||||
|  |             if row == 0 or col == 0: | ||||||
|  |                 maxes[row][col] = 0 | ||||||
|  |             elif xs[row - 1][0] <= col: | ||||||
|  |                 maxes[row][col] = max( | ||||||
|  |                     xs[row - 1][1] + maxes[row - 1][col - xs[row - 1][0]], | ||||||
|  |                     maxes[row - 1][col]) | ||||||
|  |             else: | ||||||
|  |                 maxes[row][col] = maxes[row - 1][col] | ||||||
|  | 
 | ||||||
|  |     return maxes[-1][capacity] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_one_cake(self): | ||||||
|  |         actual = knapify([(3, 10), (2, 15), (7, 2), (12, 20)], capacity=12) | ||||||
|  |         expected = None | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										82
									
								
								universe/data_structures_and_algorithms/kth-to-last.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								universe/data_structures_and_algorithms/kth-to-last.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def length(x): | ||||||
|  |     if not x: | ||||||
|  |         return 0 | ||||||
|  |     else: | ||||||
|  |         count = 1 | ||||||
|  |         while x: | ||||||
|  |             x = x.next | ||||||
|  |             count += 1 | ||||||
|  |         return count | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def kth_to_last_node(k, x): | ||||||
|  |     hops = length(x) - 1 | ||||||
|  |     dest = hops - k | ||||||
|  | 
 | ||||||
|  |     if k == 0: | ||||||
|  |         raise Exception("Our God doesn't support this kind of behavior.") | ||||||
|  | 
 | ||||||
|  |     if dest < 0: | ||||||
|  |         raise Exception('Value k to high for list.') | ||||||
|  | 
 | ||||||
|  |     while dest > 0: | ||||||
|  |         x = x.next | ||||||
|  |         dest -= 1 | ||||||
|  | 
 | ||||||
|  |     return x | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     class LinkedListNode(object): | ||||||
|  |         def __init__(self, value, next=None): | ||||||
|  |             self.value = value | ||||||
|  |             self.next = next | ||||||
|  | 
 | ||||||
|  |         def get_values(self): | ||||||
|  |             node = self | ||||||
|  |             values = [] | ||||||
|  |             while node is not None: | ||||||
|  |                 values.append(node.value) | ||||||
|  |                 node = node.next | ||||||
|  |             return values | ||||||
|  | 
 | ||||||
|  |     def setUp(self): | ||||||
|  |         self.fourth = Test.LinkedListNode(4) | ||||||
|  |         self.third = Test.LinkedListNode(3, self.fourth) | ||||||
|  |         self.second = Test.LinkedListNode(2, self.third) | ||||||
|  |         self.first = Test.LinkedListNode(1, self.second) | ||||||
|  | 
 | ||||||
|  |     def test_first_to_last_node(self): | ||||||
|  |         actual = kth_to_last_node(1, self.first) | ||||||
|  |         expected = self.fourth | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_second_to_last_node(self): | ||||||
|  |         actual = kth_to_last_node(2, self.first) | ||||||
|  |         expected = self.third | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_first_node(self): | ||||||
|  |         actual = kth_to_last_node(4, self.first) | ||||||
|  |         expected = self.first | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_k_greater_than_linked_list_length(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             kth_to_last_node(5, self.first) | ||||||
|  | 
 | ||||||
|  |     def test_k_is_zero(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             kth_to_last_node(0, self.first) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										107
									
								
								universe/data_structures_and_algorithms/largest-stack.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								universe/data_structures_and_algorithms/largest-stack.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,107 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Stack(object): | ||||||
|  |     def __init__(self): | ||||||
|  |         """Initialize an empty stack""" | ||||||
|  |         self.items = [] | ||||||
|  | 
 | ||||||
|  |     def push(self, item): | ||||||
|  |         """Push a new item onto the stack""" | ||||||
|  |         self.items.append(item) | ||||||
|  | 
 | ||||||
|  |     def pop(self): | ||||||
|  |         """Remove and return the last item""" | ||||||
|  |         # If the stack is empty, return None | ||||||
|  |         # (it would also be reasonable to throw an exception) | ||||||
|  |         if not self.items: | ||||||
|  |             return None | ||||||
|  | 
 | ||||||
|  |         return self.items.pop() | ||||||
|  | 
 | ||||||
|  |     def peek(self): | ||||||
|  |         """Return the last item without removing it""" | ||||||
|  |         if not self.items: | ||||||
|  |             return None | ||||||
|  |         return self.items[-1] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class MaxStack(object): | ||||||
|  |     # Implement the push, pop, and get_max methods | ||||||
|  |     def __init__(self): | ||||||
|  |         self.m = Stack() | ||||||
|  |         self.stack = Stack() | ||||||
|  | 
 | ||||||
|  |     def push(self, item): | ||||||
|  |         if self.m.peek() is None: | ||||||
|  |             self.m.push(item) | ||||||
|  |         elif item >= self.m.peek(): | ||||||
|  |             self.m.push(item) | ||||||
|  |         self.stack.push(item) | ||||||
|  | 
 | ||||||
|  |     def pop(self): | ||||||
|  |         x = self.stack.pop() | ||||||
|  |         if x == self.m.peek(): | ||||||
|  |             self.m.pop() | ||||||
|  |         return x | ||||||
|  | 
 | ||||||
|  |     def get_max(self): | ||||||
|  |         return self.m.peek() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_stack_usage(self): | ||||||
|  |         max_stack = MaxStack() | ||||||
|  | 
 | ||||||
|  |         max_stack.push(5) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.get_max() | ||||||
|  |         expected = 5 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |         max_stack.push(4) | ||||||
|  |         max_stack.push(7) | ||||||
|  |         max_stack.push(7) | ||||||
|  |         max_stack.push(8) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.get_max() | ||||||
|  |         expected = 8 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.pop() | ||||||
|  |         expected = 8 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.get_max() | ||||||
|  |         expected = 7 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.pop() | ||||||
|  |         expected = 7 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.get_max() | ||||||
|  |         expected = 7 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.pop() | ||||||
|  |         expected = 7 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.get_max() | ||||||
|  |         expected = 5 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.pop() | ||||||
|  |         expected = 4 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |         actual = max_stack.get_max() | ||||||
|  |         expected = 5 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,88 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def contains_cycle(x): | ||||||
|  |     if not x: | ||||||
|  |         return False | ||||||
|  |     elif not x.next: | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  |     a = x | ||||||
|  |     b = x.next | ||||||
|  | 
 | ||||||
|  |     while b.next: | ||||||
|  |         if a == b: | ||||||
|  |             return True | ||||||
|  | 
 | ||||||
|  |         a = a.next | ||||||
|  |         b = b.next.next | ||||||
|  | 
 | ||||||
|  |     return False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     class LinkedListNode(object): | ||||||
|  |         def __init__(self, value, next=None): | ||||||
|  |             self.value = value | ||||||
|  |             self.next = next | ||||||
|  | 
 | ||||||
|  |     def test_linked_list_with_no_cycle(self): | ||||||
|  |         fourth = Test.LinkedListNode(4) | ||||||
|  |         third = Test.LinkedListNode(3, fourth) | ||||||
|  |         second = Test.LinkedListNode(2, third) | ||||||
|  |         first = Test.LinkedListNode(1, second) | ||||||
|  |         result = contains_cycle(first) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_cycle_loops_to_beginning(self): | ||||||
|  |         fourth = Test.LinkedListNode(4) | ||||||
|  |         third = Test.LinkedListNode(3, fourth) | ||||||
|  |         second = Test.LinkedListNode(2, third) | ||||||
|  |         first = Test.LinkedListNode(1, second) | ||||||
|  |         fourth.next = first | ||||||
|  |         result = contains_cycle(first) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_cycle_loops_to_middle(self): | ||||||
|  |         fifth = Test.LinkedListNode(5) | ||||||
|  |         fourth = Test.LinkedListNode(4, fifth) | ||||||
|  |         third = Test.LinkedListNode(3, fourth) | ||||||
|  |         second = Test.LinkedListNode(2, third) | ||||||
|  |         first = Test.LinkedListNode(1, second) | ||||||
|  |         fifth.next = third | ||||||
|  |         result = contains_cycle(first) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_two_node_cycle_at_end(self): | ||||||
|  |         fifth = Test.LinkedListNode(5) | ||||||
|  |         fourth = Test.LinkedListNode(4, fifth) | ||||||
|  |         third = Test.LinkedListNode(3, fourth) | ||||||
|  |         second = Test.LinkedListNode(2, third) | ||||||
|  |         first = Test.LinkedListNode(1, second) | ||||||
|  |         fifth.next = fourth | ||||||
|  |         result = contains_cycle(first) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_empty_list(self): | ||||||
|  |         result = contains_cycle(None) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_one_element_linked_list_no_cycle(self): | ||||||
|  |         first = Test.LinkedListNode(1) | ||||||
|  |         result = contains_cycle(first) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_one_element_linked_list_cycle(self): | ||||||
|  |         first = Test.LinkedListNode(1) | ||||||
|  |         first.next = first | ||||||
|  |         result = contains_cycle(first) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										28
									
								
								universe/data_structures_and_algorithms/merge-sort.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								universe/data_structures_and_algorithms/merge-sort.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # merge :: [a] -> [a] -> [a] | ||||||
|  | # merge([], []): [] | ||||||
|  | # merge(xs, []): xs | ||||||
|  | # merge([], ys): ys | ||||||
|  | # merge(xs@[x|xs'], ys@[y|ys']) | ||||||
|  | #   when y =< x: cons(y, merge(xs, ys')) | ||||||
|  | #   when x < y:  cons(x, merge(xs', ys)) | ||||||
|  | def merge(xs, ys): | ||||||
|  |     if xs == [] and ys == []: | ||||||
|  |         return [] | ||||||
|  |     elif ys == []: | ||||||
|  |         return xs | ||||||
|  |     elif xs == []: | ||||||
|  |         return ys | ||||||
|  |     else: | ||||||
|  |         x = xs[0] | ||||||
|  |         y = ys[0] | ||||||
|  | 
 | ||||||
|  |         if y <= x: | ||||||
|  |             return [y] + merge(xs, ys[1:]) | ||||||
|  |         else: | ||||||
|  |             return [x] + merge(xs[1:], ys) | ||||||
|  |          | ||||||
|  | print(merge([3, 4, 6, 10, 11, 15], | ||||||
|  |             [1, 5, 8, 12, 14, 19])) | ||||||
							
								
								
									
										94
									
								
								universe/data_structures_and_algorithms/merging-ranges.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								universe/data_structures_and_algorithms/merging-ranges.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | # do_merge_ranges :: [(Int, Int)] -> [(Int, Int)] -> [(Int, Int)] | ||||||
|  | def do_merge_ranges(prev, xs): | ||||||
|  |     if len(xs) == 0: | ||||||
|  |         return prev | ||||||
|  |     elif len(xs) == 1: | ||||||
|  |         return prev + xs | ||||||
|  |     else: | ||||||
|  |         a1, a2 = xs[0] | ||||||
|  |         b1, b2 = xs[1] | ||||||
|  |         rest = xs[2:] | ||||||
|  |         if b1 <= a2: | ||||||
|  |             return do_merge_ranges(prev, [(a1, max(a2, b2))] + rest) | ||||||
|  |         else: | ||||||
|  |             return do_merge_ranges(prev + [(a1, a2)], [(b1, b2)] + rest) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # merge_ranges :: [(Int, Int)] -> [(Int, Int)] | ||||||
|  | def merge_ranges(xs): | ||||||
|  |     xs = xs[:] | ||||||
|  |     xs.sort() | ||||||
|  |     return do_merge_ranges([], xs) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # merge_ranges_b :: [(Int, Int)] -> [(Int, Int)] | ||||||
|  | def merge_ranges_b(xs): | ||||||
|  |     fi = 0 | ||||||
|  |     ci = 1 | ||||||
|  |     result = [] | ||||||
|  |     xs = xs[:] | ||||||
|  |     xs.sort() | ||||||
|  |     while ci < len(xs): | ||||||
|  |         while ci < len(xs) and xs[ci][0] <= xs[fi][1]: | ||||||
|  |             xs[fi] = xs[fi][0], max(xs[ci][1], xs[fi][1]) | ||||||
|  |             ci += 1 | ||||||
|  |         result.append(xs[fi]) | ||||||
|  |         fi = ci | ||||||
|  |         ci += 1 | ||||||
|  |     if fi < len(xs): | ||||||
|  |         result.append(xs[fi]) | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_meetings_overlap(self): | ||||||
|  |         actual = merge_ranges([(1, 3), (2, 4)]) | ||||||
|  |         expected = [(1, 4)] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_meetings_touch(self): | ||||||
|  |         actual = merge_ranges([(5, 6), (6, 8)]) | ||||||
|  |         expected = [(5, 8)] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_meeting_contains_other_meeting(self): | ||||||
|  |         actual = merge_ranges([(1, 8), (2, 5)]) | ||||||
|  |         expected = [(1, 8)] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_meetings_stay_separate(self): | ||||||
|  |         actual = merge_ranges([(1, 3), (4, 8)]) | ||||||
|  |         expected = [(1, 3), (4, 8)] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_multiple_merged_meetings(self): | ||||||
|  |         actual = merge_ranges([(1, 4), (2, 5), (5, 8)]) | ||||||
|  |         expected = [(1, 8)] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_meetings_not_sorted(self): | ||||||
|  |         actual = merge_ranges([(5, 8), (1, 4), (6, 8)]) | ||||||
|  |         expected = [(1, 4), (5, 8)] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_long_meeting_contains_smaller_meetings(self): | ||||||
|  |         actual = merge_ranges([(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)]) | ||||||
|  |         expected = [(1, 12)] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_sample_input(self): | ||||||
|  |         actual = merge_ranges([(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)]) | ||||||
|  |         expected = [(0, 1), (3, 8), (9, 12)] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										11
									
								
								universe/data_structures_and_algorithms/mesh-message.gv
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								universe/data_structures_and_algorithms/mesh-message.gv
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | strict graph { | ||||||
|  |     Min -- {William, Jayden, Omar} | ||||||
|  |     William -- {Min, Noam} | ||||||
|  |     Jayden -- {Min, Amelia, Ren, Noam} | ||||||
|  |     Adam -- {Amelia, Miguel, Sofia, Lucas} | ||||||
|  |     Ren -- {Jayden, Omar} | ||||||
|  |     Amelia -- {Jayden, Adam, Miguel} | ||||||
|  |     Miguel -- {Amelia, Adam, Liam, Nathan} | ||||||
|  |     Noam -- {Nathan, Jayden, William} | ||||||
|  |     Omar -- {Ren, Min, Scott} | ||||||
|  | } | ||||||
							
								
								
									
										97
									
								
								universe/data_structures_and_algorithms/mesh-message.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								universe/data_structures_and_algorithms/mesh-message.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,97 @@ | ||||||
|  | import unittest | ||||||
|  | from collections import deque | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | # get_path :: G(V, E) -> V -> V -> Maybe([V]) | ||||||
|  | def get_path(g, src, dst): | ||||||
|  |     q = deque() | ||||||
|  |     result = None | ||||||
|  |     seen = set() | ||||||
|  |     q.append(([], src)) | ||||||
|  | 
 | ||||||
|  |     if src not in g or dst not in g: | ||||||
|  |         raise Exception | ||||||
|  | 
 | ||||||
|  |     while q: | ||||||
|  |         p, node = q.popleft() | ||||||
|  | 
 | ||||||
|  |         seen.add(node) | ||||||
|  | 
 | ||||||
|  |         if node == dst: | ||||||
|  |             if not result: | ||||||
|  |                 result = p + [node] | ||||||
|  |             elif len(p + [node]) < len(result): | ||||||
|  |                 result = p + [node] | ||||||
|  |         else: | ||||||
|  |             if node not in g: | ||||||
|  |                 raise Exception | ||||||
|  |             for x in g.get(node): | ||||||
|  |                 if not x in seen: | ||||||
|  |                     q.append((p + [node], x)) | ||||||
|  | 
 | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def setUp(self): | ||||||
|  |         self.graph = { | ||||||
|  |             'a': ['b', 'c', 'd'], | ||||||
|  |             'b': ['a', 'd'], | ||||||
|  |             'c': ['a', 'e'], | ||||||
|  |             'd': ['a', 'b'], | ||||||
|  |             'e': ['c'], | ||||||
|  |             'f': ['g'], | ||||||
|  |             'g': ['f'], | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     def test_two_hop_path_1(self): | ||||||
|  |         actual = get_path(self.graph, 'a', 'e') | ||||||
|  |         expected = ['a', 'c', 'e'] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_two_hop_path_2(self): | ||||||
|  |         actual = get_path(self.graph, 'd', 'c') | ||||||
|  |         expected = ['d', 'a', 'c'] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_hop_path_1(self): | ||||||
|  |         actual = get_path(self.graph, 'a', 'c') | ||||||
|  |         expected = ['a', 'c'] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_hop_path_2(self): | ||||||
|  |         actual = get_path(self.graph, 'f', 'g') | ||||||
|  |         expected = ['f', 'g'] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_hop_path_3(self): | ||||||
|  |         actual = get_path(self.graph, 'g', 'f') | ||||||
|  |         expected = ['g', 'f'] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_zero_hop_path(self): | ||||||
|  |         actual = get_path(self.graph, 'a', 'a') | ||||||
|  |         expected = ['a'] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_no_path(self): | ||||||
|  |         actual = get_path(self.graph, 'a', 'f') | ||||||
|  |         expected = None | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_start_node_not_present(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             get_path(self.graph, 'h', 'a') | ||||||
|  | 
 | ||||||
|  |     def test_end_node_not_present(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             get_path(self.graph, 'a', 'h') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										78
									
								
								universe/data_structures_and_algorithms/norman.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								universe/data_structures_and_algorithms/norman.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Write a function with the following type signature:L | ||||||
|  | # equal? :: String -> String -> Bool | ||||||
|  | # | ||||||
|  | # Determine equality between two inputs with backspace characters encoded as | ||||||
|  | # "<". | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution 1 | ||||||
|  | ################################################################################ | ||||||
|  | 
 | ||||||
|  | # from collections import deque | ||||||
|  | 
 | ||||||
|  | # def equal(a, b): | ||||||
|  | #     sa = deque() | ||||||
|  | #     sb = deque() | ||||||
|  | 
 | ||||||
|  | #     for c in a: | ||||||
|  | #         if c == '<': | ||||||
|  | #             sa.pop() | ||||||
|  | #         else: | ||||||
|  | #             sa.append(c) | ||||||
|  | 
 | ||||||
|  | #     for c in b: | ||||||
|  | #         if c == '<': | ||||||
|  | #             sb.pop() | ||||||
|  | #         else: | ||||||
|  | #             sb.append(c) | ||||||
|  | 
 | ||||||
|  | #     return sa == sb | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution 2 | ||||||
|  | ################################################################################ | ||||||
|  | 
 | ||||||
|  | def handle_dels(num_dels, i, xs): | ||||||
|  |     if i < 0: | ||||||
|  |         return -1 | ||||||
|  | 
 | ||||||
|  |     while xs[i] == '<': | ||||||
|  |         num_dels += 1 | ||||||
|  |         i -= 1 | ||||||
|  | 
 | ||||||
|  |     while num_dels > 0 and xs[i] != '<': | ||||||
|  |         num_dels -= 1 | ||||||
|  |         i -= 1 | ||||||
|  | 
 | ||||||
|  |     if xs[i] == '<': | ||||||
|  |         return handle_dels(num_dels, i, xs) | ||||||
|  |     else: | ||||||
|  |         return i | ||||||
|  | 
 | ||||||
|  | def update_index(i, xs): | ||||||
|  |     # TODO: Indexing into non-available parts of a string. | ||||||
|  |     if xs[i] != '<' and xs[i - 1] != '<': | ||||||
|  |         return i - 1 | ||||||
|  | 
 | ||||||
|  |     elif xs[i - 1] == '<': | ||||||
|  |         return handle_dels(0, i - 1, xs) | ||||||
|  | 
 | ||||||
|  | def equal(a, b): | ||||||
|  |     ia = len(a) - 1 | ||||||
|  |     ib = len(b) - 1 | ||||||
|  | 
 | ||||||
|  |     while ia >= 0 and ib >= 0: | ||||||
|  |         if a[ia] != b[ib]: | ||||||
|  |             return False | ||||||
|  |         ia = update_index(ia, a) | ||||||
|  |         ib = update_index(ib, b) | ||||||
|  | 
 | ||||||
|  |     if ia != 0: | ||||||
|  |         return update_index(ia, a) <= -1 | ||||||
|  |     if ib != 0: | ||||||
|  |         return update_index(ib, b) <= -1 | ||||||
|  | 
 | ||||||
|  |     return True | ||||||
							
								
								
									
										59
									
								
								universe/data_structures_and_algorithms/nth-fibonacci.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								universe/data_structures_and_algorithms/nth-fibonacci.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def fib(n): | ||||||
|  |     """This should be accomplishable in O(1) space.""" | ||||||
|  |     if n in {0, 1}: | ||||||
|  |         return n | ||||||
|  |     a = 0  # i = 0 | ||||||
|  |     b = 1  # i = 1 | ||||||
|  |     for x in range(2, n + 1): | ||||||
|  |         result = a + b | ||||||
|  |         a = b | ||||||
|  |         b = result | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_zeroth_fibonacci(self): | ||||||
|  |         actual = fib(0) | ||||||
|  |         expected = 0 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_first_fibonacci(self): | ||||||
|  |         actual = fib(1) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_second_fibonacci(self): | ||||||
|  |         actual = fib(2) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_third_fibonacci(self): | ||||||
|  |         actual = fib(3) | ||||||
|  |         expected = 2 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_fifth_fibonacci(self): | ||||||
|  |         actual = fib(5) | ||||||
|  |         expected = 5 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_tenth_fibonacci(self): | ||||||
|  |         actual = fib(10) | ||||||
|  |         expected = 55 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_negative_fibonacci(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             fib(-1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										49
									
								
								universe/data_structures_and_algorithms/optimal-stopping.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								universe/data_structures_and_algorithms/optimal-stopping.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | from random import choice | ||||||
|  | from math import floor | ||||||
|  | 
 | ||||||
|  | # Applying Chapter 1 from "Algorithms to Live By", which describes optimal | ||||||
|  | # stopping problems. Technically this simulation is invalid because the | ||||||
|  | # `candidates` function takes a lower bound and an upper bound, which allows us | ||||||
|  | # to know the cardinal number of an individual candidates. The "look then leap" | ||||||
|  | # algorithm is ideal for no-information games - i.e. games when upper and lower | ||||||
|  | # bounds aren't known. The `look_then_leap/1` function is ignorant of this | ||||||
|  | # information, so it behaves as if in a no-information game. Strangely enough, | ||||||
|  | # this algorithm will pick the best candidate 37% of the time. | ||||||
|  | # | ||||||
|  | # Chapter 1 describes two algorithms: | ||||||
|  | # 1. Look-then-leap: ordinal numbers - i.e. no-information games. Look-then-leap | ||||||
|  | #    finds the best candidate 37% of the time. | ||||||
|  | # 2. Threshold: cardinal numbers - i.e. where upper and lower bounds are | ||||||
|  | #    known. The Threshold algorithm finds the best candidate ~55% of the time. | ||||||
|  | # | ||||||
|  | # All of this and more can be studied as "optimal stopping theory". This applies | ||||||
|  | # to finding a spouse, parking a car, picking an apartment in a city, and more. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # candidates :: Int -> Int -> Int -> [Int] | ||||||
|  | def candidates(lb, ub, ct): | ||||||
|  |     xs = list(range(lb, ub + 1)) | ||||||
|  |     return [choice(xs) for _ in range(ct)] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # look_then_leap :: [Integer] -> Integer | ||||||
|  | def look_then_leap(candidates): | ||||||
|  |     best = candidates[0] | ||||||
|  |     seen_ct = 1 | ||||||
|  |     ignore_ct = floor(len(candidates) * 0.37) | ||||||
|  |     for x in candidates[1:]: | ||||||
|  |         if ignore_ct > 0: | ||||||
|  |             ignore_ct -= 1 | ||||||
|  |             best = max(best, x) | ||||||
|  |         else: | ||||||
|  |             if x > best: | ||||||
|  |                 print('Choosing the {} candidate.'.format(seen_ct)) | ||||||
|  |                 return x | ||||||
|  |         seen_ct += 1 | ||||||
|  |     print('You may have waited too long.') | ||||||
|  |     return candidates[-1] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | candidates = candidates(1, 100, 100) | ||||||
|  | print(candidates) | ||||||
|  | print(look_then_leap(candidates)) | ||||||
							
								
								
									
										83
									
								
								universe/data_structures_and_algorithms/perm-tree.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								universe/data_structures_and_algorithms/perm-tree.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Answer | ||||||
|  | ################################################################################ | ||||||
|  | class Node(object): | ||||||
|  |     def __init__(self, value, children=set()): | ||||||
|  |         self.value = value | ||||||
|  |         self.children = children | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # treeify :: Char -> Set(Char) -> Node(Char) | ||||||
|  | def treeify(x, xs): | ||||||
|  |     return Node(x, [treeify(c, xs - {c}) for c in xs]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # dft :: Node(Char) -> [String] | ||||||
|  | def dft(node): | ||||||
|  |     result = [] | ||||||
|  |     s = [] | ||||||
|  | 
 | ||||||
|  |     s.append(('', node)) | ||||||
|  | 
 | ||||||
|  |     while s: | ||||||
|  |         p, n = s.pop() | ||||||
|  |         p += str(n.value) | ||||||
|  | 
 | ||||||
|  |         if not n.children: | ||||||
|  |             result.append(p) | ||||||
|  |         else: | ||||||
|  |             for c in n.children: | ||||||
|  |                 s.append((p, c)) | ||||||
|  | 
 | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # main :: String -> Set(String) | ||||||
|  | def get_permutations(xs): | ||||||
|  |     if xs == '': | ||||||
|  |         return set(['']) | ||||||
|  | 
 | ||||||
|  |     ys = set(xs) | ||||||
|  |     trees = [] | ||||||
|  | 
 | ||||||
|  |     for y in ys: | ||||||
|  |         trees.append(treeify(y, ys - {y})) | ||||||
|  | 
 | ||||||
|  |     result = set() | ||||||
|  | 
 | ||||||
|  |     for t in trees: | ||||||
|  |         for d in dft(t): | ||||||
|  |             result.add(d) | ||||||
|  | 
 | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_empty_string(self): | ||||||
|  |         actual = get_permutations('') | ||||||
|  |         expected = set(['']) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_character_string(self): | ||||||
|  |         actual = get_permutations('a') | ||||||
|  |         expected = set(['a']) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_two_character_string(self): | ||||||
|  |         actual = get_permutations('ab') | ||||||
|  |         expected = set(['ab', 'ba']) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_three_character_string(self): | ||||||
|  |         actual = get_permutations('abc') | ||||||
|  |         expected = set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba']) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,49 @@ | ||||||
|  | from collections import Counter | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Impl | ||||||
|  | ################################################################################ | ||||||
|  | # palindromifiable :: String -> Boolean | ||||||
|  | def has_palindrome_permutation(x): | ||||||
|  |     bag = Counter(x) | ||||||
|  |     odd_entries_ct = 0 | ||||||
|  | 
 | ||||||
|  |     for _, y in bag.items(): | ||||||
|  |         if y % 2 != 0: | ||||||
|  |             odd_entries_ct += 1 | ||||||
|  | 
 | ||||||
|  |     return odd_entries_ct in {0, 1} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_permutation_with_odd_number_of_chars(self): | ||||||
|  |         result = has_palindrome_permutation('aabcbcd') | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_permutation_with_even_number_of_chars(self): | ||||||
|  |         result = has_palindrome_permutation('aabccbdd') | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_no_permutation_with_odd_number_of_chars(self): | ||||||
|  |         result = has_palindrome_permutation('aabcd') | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_no_permutation_with_even_number_of_chars(self): | ||||||
|  |         result = has_palindrome_permutation('aabbcd') | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_empty_string(self): | ||||||
|  |         result = has_palindrome_permutation('') | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_one_character_string(self): | ||||||
|  |         result = has_palindrome_permutation('a') | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										55
									
								
								universe/data_structures_and_algorithms/permutations.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								universe/data_structures_and_algorithms/permutations.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | ||||||
|  | class Node(object): | ||||||
|  |     # ctor :: a -> [a] -> Node(a) | ||||||
|  |     def __init__(self, value, children=[]): | ||||||
|  |         self.value = value | ||||||
|  |         self.children = children | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # is_leaf :: Node(a) -> Boolean | ||||||
|  | def is_leaf(node): | ||||||
|  |     return len(node.children) == 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # enumerate :: Node(a) -> Set(List(a)) | ||||||
|  | def enumerate(node): | ||||||
|  |     current = [] | ||||||
|  |     result = [] | ||||||
|  |     q = [] | ||||||
|  | 
 | ||||||
|  |     q.append(node) | ||||||
|  | 
 | ||||||
|  |     while q: | ||||||
|  |         x = q.pop() | ||||||
|  |         print(x.value) | ||||||
|  | 
 | ||||||
|  |         for c in x.children: | ||||||
|  |             q.append(c) | ||||||
|  | 
 | ||||||
|  |         current.append(x.value) | ||||||
|  |         print(current) | ||||||
|  | 
 | ||||||
|  |         if is_leaf(x): | ||||||
|  |             result.append(current) | ||||||
|  |             print("Reseting current") | ||||||
|  |             current = [] | ||||||
|  | 
 | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | node = Node('root', [ | ||||||
|  |     Node('a', [ | ||||||
|  |         Node('b', [Node('c')]), | ||||||
|  |         Node('c', [Node('b')]), | ||||||
|  |     ]), | ||||||
|  |     Node('b', [ | ||||||
|  |         Node('a', [Node('c')]), | ||||||
|  |         Node('c', [Node('a')]), | ||||||
|  |     ]), | ||||||
|  |     Node('c', [ | ||||||
|  |         Node('a', [Node('b')]), | ||||||
|  |         Node('b', [Node('a')]), | ||||||
|  |     ]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | print('----------') | ||||||
|  | print(enumerate(node)) | ||||||
							
								
								
									
										9
									
								
								universe/data_structures_and_algorithms/plot.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								universe/data_structures_and_algorithms/plot.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | import numpy as np | ||||||
|  | import matplotlib.pyplot as plt | ||||||
|  | 
 | ||||||
|  | rng = np.random.RandomState(10)  # deterministic random data | ||||||
|  | a = np.hstack((rng.normal(size=1000), rng.normal(loc=5, scale=2, size=1000))) | ||||||
|  | _ = plt.hist(a, bins='auto')  # arguments are passed to np.histogram | ||||||
|  | plt.title("Histogram with 'auto' bins") | ||||||
|  | Text(0.5, 1.0, "Histogram with 'auto' bins") | ||||||
|  | plt.show() | ||||||
|  | @ -0,0 +1,68 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | # f :: [Int] -> [Int] | ||||||
|  | def get_products_of_all_ints_except_at_index(xs): | ||||||
|  |     if len(xs) in {0, 1}: | ||||||
|  |         raise Exception | ||||||
|  | 
 | ||||||
|  |     ct = len(xs) | ||||||
|  |     lefts = [1] * ct | ||||||
|  |     rights = [1] * ct | ||||||
|  |     result = [] | ||||||
|  | 
 | ||||||
|  |     for i in range(1, ct): | ||||||
|  |         lefts[i] = lefts[i - 1] * xs[i - 1] | ||||||
|  |     for i in range(ct - 2, -1, -1): | ||||||
|  |         rights[i] = rights[i + 1] * xs[i + 1] | ||||||
|  | 
 | ||||||
|  |     return [lefts[i] * rights[i] for i in range(ct)] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_small_list(self): | ||||||
|  |         actual = get_products_of_all_ints_except_at_index([1, 2, 3]) | ||||||
|  |         expected = [6, 3, 2] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_longer_list(self): | ||||||
|  |         actual = get_products_of_all_ints_except_at_index([8, 2, 4, 3, 1, 5]) | ||||||
|  |         expected = [120, 480, 240, 320, 960, 192] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_list_has_one_zero(self): | ||||||
|  |         actual = get_products_of_all_ints_except_at_index([6, 2, 0, 3]) | ||||||
|  |         expected = [0, 0, 36, 0] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_list_has_two_zeros(self): | ||||||
|  |         actual = get_products_of_all_ints_except_at_index([4, 0, 9, 1, 0]) | ||||||
|  |         expected = [0, 0, 0, 0, 0] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_negative_number(self): | ||||||
|  |         actual = get_products_of_all_ints_except_at_index([-3, 8, 4]) | ||||||
|  |         expected = [32, -12, -24] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_all_negative_numbers(self): | ||||||
|  |         actual = get_products_of_all_ints_except_at_index([-7, -1, -4, -2]) | ||||||
|  |         expected = [-8, -56, -14, -28] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_error_with_empty_list(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             get_products_of_all_ints_except_at_index([]) | ||||||
|  | 
 | ||||||
|  |     def test_error_with_one_number(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             get_products_of_all_ints_except_at_index([1]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										66
									
								
								universe/data_structures_and_algorithms/queue-two-stacks.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								universe/data_structures_and_algorithms/queue-two-stacks.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | class QueueTwoStacks(object): | ||||||
|  |     def __init__(self): | ||||||
|  |         self.a = [] | ||||||
|  |         self.b = [] | ||||||
|  | 
 | ||||||
|  |     def enqueue(self, x): | ||||||
|  |         self.a.append(x) | ||||||
|  | 
 | ||||||
|  |     def dequeue(self): | ||||||
|  |         if self.b: | ||||||
|  |             return self.b.pop() | ||||||
|  |         else: | ||||||
|  |             while self.a: | ||||||
|  |                 self.b.append(self.a.pop()) | ||||||
|  |             return self.dequeue() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_basic_queue_operations(self): | ||||||
|  |         queue = QueueTwoStacks() | ||||||
|  |         queue.enqueue(1) | ||||||
|  |         queue.enqueue(2) | ||||||
|  |         queue.enqueue(3) | ||||||
|  |         actual = queue.dequeue() | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  |         actual = queue.dequeue() | ||||||
|  |         expected = 2 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  |         queue.enqueue(4) | ||||||
|  |         actual = queue.dequeue() | ||||||
|  |         expected = 3 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  |         actual = queue.dequeue() | ||||||
|  |         expected = 4 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_error_when_dequeue_from_new_queue(self): | ||||||
|  |         queue = QueueTwoStacks() | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             queue.dequeue() | ||||||
|  | 
 | ||||||
|  |     def test_error_when_dequeue_from_empty_queue(self): | ||||||
|  |         queue = QueueTwoStacks() | ||||||
|  |         queue.enqueue(1) | ||||||
|  |         queue.enqueue(2) | ||||||
|  |         actual = queue.dequeue() | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  |         actual = queue.dequeue() | ||||||
|  |         expected = 2 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             queue.dequeue() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										246
									
								
								universe/data_structures_and_algorithms/rectangular-love.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								universe/data_structures_and_algorithms/rectangular-love.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,246 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | # bottom :: Rectangle -> Int | ||||||
|  | def bottom(x): | ||||||
|  |     return x.get('bottom_y') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # top :: Rectangle -> Int | ||||||
|  | def top(x): | ||||||
|  |     return bottom(x) + x.get('height') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # left :: Rectangle -> Int | ||||||
|  | def left(x): | ||||||
|  |     return x.get('left_x') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # right :: Rectangle -> Int | ||||||
|  | def right(x): | ||||||
|  |     return left(x) + x.get('width') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # sort_highest :: Rectangle -> Rectangle -> (Rectangle, Rectangle) | ||||||
|  | def sort_highest(x, y): | ||||||
|  |     if top(x) >= top(y): | ||||||
|  |         return x, y | ||||||
|  |     else: | ||||||
|  |         return y, x | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # sort_leftmost :: Rectangle -> Rectangle -> (Rectangle, Rectangle) | ||||||
|  | def sort_leftmost(x, y): | ||||||
|  |     if left(x) <= left(y): | ||||||
|  |         return x, y | ||||||
|  |     else: | ||||||
|  |         return y, x | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # rectify :: Int -> Int -> Int -> Int -> Rectify | ||||||
|  | def rectify(top=None, bottom=None, left=None, right=None): | ||||||
|  |     assert top >= bottom | ||||||
|  |     assert left <= right | ||||||
|  |     return { | ||||||
|  |         'left_x': left, | ||||||
|  |         'bottom_y': bottom, | ||||||
|  |         'width': right - left, | ||||||
|  |         'height': top - bottom, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # empty_rect :: Rectangle | ||||||
|  | def empty_rect(): | ||||||
|  |     return { | ||||||
|  |         'left_x': None, | ||||||
|  |         'bottom_y': None, | ||||||
|  |         'width': None, | ||||||
|  |         'height': None, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # find_rectangular_overlap :: Rectangle -> Rectangle -> Maybe(Rectangle) | ||||||
|  | def find_rectangular_overlap(x, y): | ||||||
|  |     ha, hb = sort_highest(x, y) | ||||||
|  |     la, lb = sort_leftmost(x, y) | ||||||
|  | 
 | ||||||
|  |     if bottom(hb) <= top(hb) <= bottom(ha) <= top(ha): | ||||||
|  |         return empty_rect() | ||||||
|  | 
 | ||||||
|  |     if left(la) <= right(la) <= left(lb) <= right(lb): | ||||||
|  |         return empty_rect() | ||||||
|  | 
 | ||||||
|  |     # We should have an intersection here. | ||||||
|  |     verts = [bottom(ha), top(ha), bottom(hb), top(hb)] | ||||||
|  |     verts.sort() | ||||||
|  |     horzs = [left(la), right(la), left(lb), right(lb)] | ||||||
|  |     horzs.sort() | ||||||
|  |     return rectify(top=verts[2], | ||||||
|  |                    bottom=verts[1], | ||||||
|  |                    left=horzs[1], | ||||||
|  |                    right=horzs[2]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_overlap_along_both_axes(self): | ||||||
|  |         rect1 = { | ||||||
|  |             'left_x': 1, | ||||||
|  |             'bottom_y': 1, | ||||||
|  |             'width': 6, | ||||||
|  |             'height': 3, | ||||||
|  |         } | ||||||
|  |         rect2 = { | ||||||
|  |             'left_x': 5, | ||||||
|  |             'bottom_y': 2, | ||||||
|  |             'width': 3, | ||||||
|  |             'height': 6, | ||||||
|  |         } | ||||||
|  |         expected = { | ||||||
|  |             'left_x': 5, | ||||||
|  |             'bottom_y': 2, | ||||||
|  |             'width': 2, | ||||||
|  |             'height': 2, | ||||||
|  |         } | ||||||
|  |         actual = find_rectangular_overlap(rect1, rect2) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_rectangle_inside_another(self): | ||||||
|  |         rect1 = { | ||||||
|  |             'left_x': 1, | ||||||
|  |             'bottom_y': 1, | ||||||
|  |             'width': 6, | ||||||
|  |             'height': 6, | ||||||
|  |         } | ||||||
|  |         rect2 = { | ||||||
|  |             'left_x': 3, | ||||||
|  |             'bottom_y': 3, | ||||||
|  |             'width': 2, | ||||||
|  |             'height': 2, | ||||||
|  |         } | ||||||
|  |         expected = { | ||||||
|  |             'left_x': 3, | ||||||
|  |             'bottom_y': 3, | ||||||
|  |             'width': 2, | ||||||
|  |             'height': 2, | ||||||
|  |         } | ||||||
|  |         actual = find_rectangular_overlap(rect1, rect2) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_both_rectangles_the_same(self): | ||||||
|  |         rect1 = { | ||||||
|  |             'left_x': 2, | ||||||
|  |             'bottom_y': 2, | ||||||
|  |             'width': 4, | ||||||
|  |             'height': 4, | ||||||
|  |         } | ||||||
|  |         rect2 = { | ||||||
|  |             'left_x': 2, | ||||||
|  |             'bottom_y': 2, | ||||||
|  |             'width': 4, | ||||||
|  |             'height': 4, | ||||||
|  |         } | ||||||
|  |         expected = { | ||||||
|  |             'left_x': 2, | ||||||
|  |             'bottom_y': 2, | ||||||
|  |             'width': 4, | ||||||
|  |             'height': 4, | ||||||
|  |         } | ||||||
|  |         actual = find_rectangular_overlap(rect1, rect2) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_touch_on_horizontal_edge(self): | ||||||
|  |         rect1 = { | ||||||
|  |             'left_x': 1, | ||||||
|  |             'bottom_y': 2, | ||||||
|  |             'width': 3, | ||||||
|  |             'height': 4, | ||||||
|  |         } | ||||||
|  |         rect2 = { | ||||||
|  |             'left_x': 2, | ||||||
|  |             'bottom_y': 6, | ||||||
|  |             'width': 2, | ||||||
|  |             'height': 2, | ||||||
|  |         } | ||||||
|  |         expected = { | ||||||
|  |             'left_x': None, | ||||||
|  |             'bottom_y': None, | ||||||
|  |             'width': None, | ||||||
|  |             'height': None, | ||||||
|  |         } | ||||||
|  |         actual = find_rectangular_overlap(rect1, rect2) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_touch_on_vertical_edge(self): | ||||||
|  |         rect1 = { | ||||||
|  |             'left_x': 1, | ||||||
|  |             'bottom_y': 2, | ||||||
|  |             'width': 3, | ||||||
|  |             'height': 4, | ||||||
|  |         } | ||||||
|  |         rect2 = { | ||||||
|  |             'left_x': 4, | ||||||
|  |             'bottom_y': 3, | ||||||
|  |             'width': 2, | ||||||
|  |             'height': 2, | ||||||
|  |         } | ||||||
|  |         expected = { | ||||||
|  |             'left_x': None, | ||||||
|  |             'bottom_y': None, | ||||||
|  |             'width': None, | ||||||
|  |             'height': None, | ||||||
|  |         } | ||||||
|  |         actual = find_rectangular_overlap(rect1, rect2) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_touch_at_a_corner(self): | ||||||
|  |         rect1 = { | ||||||
|  |             'left_x': 1, | ||||||
|  |             'bottom_y': 1, | ||||||
|  |             'width': 2, | ||||||
|  |             'height': 2, | ||||||
|  |         } | ||||||
|  |         rect2 = { | ||||||
|  |             'left_x': 3, | ||||||
|  |             'bottom_y': 3, | ||||||
|  |             'width': 2, | ||||||
|  |             'height': 2, | ||||||
|  |         } | ||||||
|  |         expected = { | ||||||
|  |             'left_x': None, | ||||||
|  |             'bottom_y': None, | ||||||
|  |             'width': None, | ||||||
|  |             'height': None, | ||||||
|  |         } | ||||||
|  |         actual = find_rectangular_overlap(rect1, rect2) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_no_overlap(self): | ||||||
|  |         rect1 = { | ||||||
|  |             'left_x': 1, | ||||||
|  |             'bottom_y': 1, | ||||||
|  |             'width': 2, | ||||||
|  |             'height': 2, | ||||||
|  |         } | ||||||
|  |         rect2 = { | ||||||
|  |             'left_x': 4, | ||||||
|  |             'bottom_y': 6, | ||||||
|  |             'width': 3, | ||||||
|  |             'height': 6, | ||||||
|  |         } | ||||||
|  |         expected = { | ||||||
|  |             'left_x': None, | ||||||
|  |             'bottom_y': None, | ||||||
|  |             'width': None, | ||||||
|  |             'height': None, | ||||||
|  |         } | ||||||
|  |         actual = find_rectangular_overlap(rect1, rect2) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Implementation | ||||||
|  | ################################################################################ | ||||||
|  | # get_permutations :: String -> Set(String) | ||||||
|  | def get_permutations(string): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_empty_string(self): | ||||||
|  |         actual = get_permutations('') | ||||||
|  |         expected = set(['']) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_character_string(self): | ||||||
|  |         actual = get_permutations('a') | ||||||
|  |         expected = set(['a']) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_two_character_string(self): | ||||||
|  |         actual = get_permutations('ab') | ||||||
|  |         expected = set(['ab', 'ba']) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_three_character_string(self): | ||||||
|  |         actual = get_permutations('abc') | ||||||
|  |         expected = set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba']) | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,79 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | # reverse :: List(a) -> List(a) | ||||||
|  | def reverse(node): | ||||||
|  |     curr = node | ||||||
|  |     prev = None | ||||||
|  |     while curr: | ||||||
|  |         nxt = curr.next | ||||||
|  |         curr.next = prev | ||||||
|  |         prev = curr | ||||||
|  |         curr = nxt | ||||||
|  |     # Make sure to understand the spec! Debugging takes time. Rewriting takes | ||||||
|  |     # time. | ||||||
|  |     return prev | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     class LinkedListNode(object): | ||||||
|  |         def __init__(self, value, next=None): | ||||||
|  |             self.value = value | ||||||
|  |             self.next = next | ||||||
|  | 
 | ||||||
|  |         def get_values(self): | ||||||
|  |             node = self | ||||||
|  |             values = [] | ||||||
|  |             while node is not None: | ||||||
|  |                 values.append(node.value) | ||||||
|  |                 node = node.next | ||||||
|  |             return values | ||||||
|  | 
 | ||||||
|  |     def test_short_linked_list(self): | ||||||
|  |         second = Test.LinkedListNode(2) | ||||||
|  |         first = Test.LinkedListNode(1, second) | ||||||
|  | 
 | ||||||
|  |         result = reverse(first) | ||||||
|  |         self.assertIsNotNone(result) | ||||||
|  | 
 | ||||||
|  |         actual = result.get_values() | ||||||
|  |         expected = [2, 1] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_long_linked_list(self): | ||||||
|  |         sixth = Test.LinkedListNode(6) | ||||||
|  |         fifth = Test.LinkedListNode(5, sixth) | ||||||
|  |         fourth = Test.LinkedListNode(4, fifth) | ||||||
|  |         third = Test.LinkedListNode(3, fourth) | ||||||
|  |         second = Test.LinkedListNode(2, third) | ||||||
|  |         first = Test.LinkedListNode(1, second) | ||||||
|  | 
 | ||||||
|  |         result = reverse(first) | ||||||
|  |         self.assertIsNotNone(result) | ||||||
|  | 
 | ||||||
|  |         actual = result.get_values() | ||||||
|  |         expected = [6, 5, 4, 3, 2, 1] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_one_element_linked_list(self): | ||||||
|  |         first = Test.LinkedListNode(1) | ||||||
|  | 
 | ||||||
|  |         result = reverse(first) | ||||||
|  |         self.assertIsNotNone(result) | ||||||
|  | 
 | ||||||
|  |         actual = result.get_values() | ||||||
|  |         expected = [1] | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_empty_linked_list(self): | ||||||
|  |         result = reverse(None) | ||||||
|  |         self.assertIsNone(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										181
									
								
								universe/data_structures_and_algorithms/reverse-words.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								universe/data_structures_and_algorithms/reverse-words.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,181 @@ | ||||||
|  | from collections import deque | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def rev(xs, i, j): | ||||||
|  |     """Reverse xs in place from [i, j]""" | ||||||
|  |     while i < j: | ||||||
|  |         xs[i], xs[j] = xs[j], xs[i] | ||||||
|  |         i += 1 | ||||||
|  |         j -= 1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def rotate(xs, n, i=None, j=None): | ||||||
|  |     """Mutably rotates list, xs, n times. Positive n values rotate right while | ||||||
|  |     negative n values rotate left. Rotate within window [i, j].""" | ||||||
|  |     i = i or 0 | ||||||
|  |     j = j or len(xs) - 1 | ||||||
|  |     ct = j - i | ||||||
|  | 
 | ||||||
|  |     if n < 0: | ||||||
|  |         n = abs(n) | ||||||
|  |         p = i + n - 1 | ||||||
|  |         rev(xs, i, p) | ||||||
|  |         rev(xs, p + 1, j) | ||||||
|  |         rev(xs, i, j) | ||||||
|  |     else: | ||||||
|  |         p = j - (n - 1) | ||||||
|  |         rev(xs, p, j) | ||||||
|  |         rev(xs, i, p - 1) | ||||||
|  |         rev(xs, i, j) | ||||||
|  |     return xs | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def rev_words(xs, i, j): | ||||||
|  |     if j + 1 == len(xs): | ||||||
|  |         return 0 | ||||||
|  | 
 | ||||||
|  |     while j + 1 < len(xs): | ||||||
|  |         while j + 1 < len(xs) and xs[j + 1] != ' ': | ||||||
|  |             j += 1 | ||||||
|  | 
 | ||||||
|  |         rev(xs, i, j) | ||||||
|  |         j += 2 | ||||||
|  |         i = j | ||||||
|  | 
 | ||||||
|  |     return 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def reverse_words(xs): | ||||||
|  |     # first reverse everything | ||||||
|  |     rev(xs, 0, len(xs) - 1) | ||||||
|  |     return rev_words(xs, 0, 0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def reverse_words_bak(xs, i=None, j=None): | ||||||
|  |     i = i or 0 | ||||||
|  |     j = j or len(xs) - 1 | ||||||
|  |     w0, w1 = [], [] | ||||||
|  | 
 | ||||||
|  |     if i >= j: | ||||||
|  |         return 0 | ||||||
|  | 
 | ||||||
|  |     pi = i | ||||||
|  |     while pi < len(xs) and xs[pi] != ' ': | ||||||
|  |         w0.append(xs[pi]) | ||||||
|  |         pi += 1 | ||||||
|  | 
 | ||||||
|  |     if pi == len(xs): | ||||||
|  |         return 0 | ||||||
|  | 
 | ||||||
|  |     pj = j | ||||||
|  |     while xs[pj] != ' ': | ||||||
|  |         w1.append(xs[pj]) | ||||||
|  |         pj -= 1 | ||||||
|  | 
 | ||||||
|  |     d = len(w0) - len(w1) | ||||||
|  | 
 | ||||||
|  |     rotate(xs, -1 * d, i, j) | ||||||
|  | 
 | ||||||
|  |     for k in range(len(w1)): | ||||||
|  |         xs[i + k] = w1[len(w1) - 1 - k] | ||||||
|  | 
 | ||||||
|  |     for k in range(len(w0)): | ||||||
|  |         xs[j - k] = w0[len(w0) - 1 - k] | ||||||
|  | 
 | ||||||
|  |     while i != j and xs[i] != ' ' and xs[j] != ' ': | ||||||
|  |         i += 1 | ||||||
|  |         j -= 1 | ||||||
|  | 
 | ||||||
|  |     if i == j: | ||||||
|  |         return 0 | ||||||
|  | 
 | ||||||
|  |     elif xs[i] == ' ': | ||||||
|  |         while j > 0 and xs[j] != ' ': | ||||||
|  |             j -= 1 | ||||||
|  |         if j == 0: | ||||||
|  |             return 0 | ||||||
|  |     elif xs[j] == ' ': | ||||||
|  |         while i < len(xs) and xs[i] != ' ': | ||||||
|  |             i += 1 | ||||||
|  |         if i == len(xs): | ||||||
|  |             return 0 | ||||||
|  |     return reverse_words(xs, i + 1, j - 1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_rev(self): | ||||||
|  |         xs = [1, 2, 3, 4, 5] | ||||||
|  |         rev(xs, 0, len(xs) - 1) | ||||||
|  |         self.assertEqual(xs, [5, 4, 3, 2, 1]) | ||||||
|  | 
 | ||||||
|  |     def test_rotate(self): | ||||||
|  |         ys = [1, 2, 3, 4, 5] | ||||||
|  |         xs = ys[:] | ||||||
|  |         self.assertEqual(rotate(xs, 1, 1, 3), [1, 4, 2, 3, 5]) | ||||||
|  |         xs = ys[:] | ||||||
|  |         self.assertEqual(rotate(xs, -1, 1, 3), [1, 3, 4, 2, 5]) | ||||||
|  |         xs = ys[:] | ||||||
|  |         self.assertEqual(rotate(xs, 1), [5, 1, 2, 3, 4]) | ||||||
|  |         xs = ys[:] | ||||||
|  |         self.assertEqual(rotate(xs, -1), [2, 3, 4, 5, 1]) | ||||||
|  |         xs = ys[:] | ||||||
|  |         self.assertEqual(rotate(xs, -2), [3, 4, 5, 1, 2]) | ||||||
|  |         xs = ys[:] | ||||||
|  |         self.assertEqual(rotate(xs, -5), [1, 2, 3, 4, 5]) | ||||||
|  |         xs = ys[:] | ||||||
|  |         self.assertEqual(rotate(xs, 5), [1, 2, 3, 4, 5]) | ||||||
|  |         xs = ys[:] | ||||||
|  |         self.assertEqual(rotate(xs, 3), [3, 4, 5, 1, 2]) | ||||||
|  | 
 | ||||||
|  |     def test_one_word(self): | ||||||
|  |         message = list('vault') | ||||||
|  |         reverse_words(message) | ||||||
|  |         expected = list('vault') | ||||||
|  |         self.assertEqual(message, expected) | ||||||
|  | 
 | ||||||
|  |     def test_two_words(self): | ||||||
|  |         message = list('thief cake') | ||||||
|  |         reverse_words(message) | ||||||
|  |         expected = list('cake thief') | ||||||
|  |         self.assertEqual(message, expected) | ||||||
|  | 
 | ||||||
|  |     def test_three_words(self): | ||||||
|  |         message = list('one another get') | ||||||
|  |         reverse_words(message) | ||||||
|  |         expected = list('get another one') | ||||||
|  |         self.assertEqual(message, expected) | ||||||
|  | 
 | ||||||
|  |     def test_multiple_words_same_length(self): | ||||||
|  |         message = list('rat the ate cat the') | ||||||
|  |         reverse_words(message) | ||||||
|  |         expected = list('the cat ate the rat') | ||||||
|  |         self.assertEqual(message, expected) | ||||||
|  | 
 | ||||||
|  |     def test_multiple_words_different_lengths(self): | ||||||
|  |         message = list('at rat house') | ||||||
|  |         reverse_words(message) | ||||||
|  |         expected = list('house rat at') | ||||||
|  |         self.assertEqual(message, expected) | ||||||
|  | 
 | ||||||
|  |     def test_multiple_words_different_lengths(self): | ||||||
|  |         message = list('yummy is cake bundt chocolate') | ||||||
|  |         reverse_words(message) | ||||||
|  |         expected = list('chocolate bundt cake is yummy') | ||||||
|  |         self.assertEqual(message, expected) | ||||||
|  | 
 | ||||||
|  |     def test_empty_string(self): | ||||||
|  |         message = list('') | ||||||
|  |         reverse_words(message) | ||||||
|  |         expected = list('') | ||||||
|  |         self.assertEqual(message, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,179 @@ | ||||||
|  | import unittest | ||||||
|  | from collections import deque | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Implementation | ||||||
|  | ################################################################################ | ||||||
|  | def is_leaf(node): | ||||||
|  |     return node.left is None and node.right is None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def find_largest(node): | ||||||
|  |     current = node | ||||||
|  |     while current.right is not None: | ||||||
|  |         current = current.right | ||||||
|  |     return current.value | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def find_second_largest(node): | ||||||
|  |     history = deque() | ||||||
|  |     current = node | ||||||
|  | 
 | ||||||
|  |     while current.right: | ||||||
|  |         history.append(current) | ||||||
|  |         current = current.right | ||||||
|  | 
 | ||||||
|  |     if current.left: | ||||||
|  |         return find_largest(current.left) | ||||||
|  |     elif history: | ||||||
|  |         return history.pop().value | ||||||
|  |     else: | ||||||
|  |         raise TypeError | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def find_second_largest_backup(node): | ||||||
|  |     history = deque() | ||||||
|  |     current = node | ||||||
|  | 
 | ||||||
|  |     # traverse -> largest | ||||||
|  |     while current.right: | ||||||
|  |         history.append(current) | ||||||
|  |         current = current.right | ||||||
|  | 
 | ||||||
|  |     if current.left: | ||||||
|  |         return find_largest(current.left) | ||||||
|  |     elif history: | ||||||
|  |         return history.pop().value | ||||||
|  |     else: | ||||||
|  |         raise ArgumentError | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Write a iterative version to avoid consuming memory with the call stack. | ||||||
|  | # Commenting out the recursive code for now. | ||||||
|  | def find_second_largest_backup(node): | ||||||
|  |     if node.left is None and node.right is None: | ||||||
|  |         raise ArgumentError | ||||||
|  | 
 | ||||||
|  |     elif node.right is None and is_leaf(node.left): | ||||||
|  |         return node.left.value | ||||||
|  | 
 | ||||||
|  |     # recursion | ||||||
|  |     # elif node.right is None: | ||||||
|  |     #     return find_largest(node.left) | ||||||
|  | 
 | ||||||
|  |     # iterative version | ||||||
|  |     elif node.right is None: | ||||||
|  |         current = node.left | ||||||
|  |         while current.right is not None: | ||||||
|  |             current = current.right | ||||||
|  |         return current.value | ||||||
|  | 
 | ||||||
|  |     # recursion | ||||||
|  |     # TODO: Remove recursion from here. | ||||||
|  |     elif not is_leaf(node.right): | ||||||
|  |         return find_second_largest(node.right) | ||||||
|  | 
 | ||||||
|  |     # could do an else here, but let's be more assertive. | ||||||
|  |     elif is_leaf(node.right): | ||||||
|  |         return node.value | ||||||
|  | 
 | ||||||
|  |     else: | ||||||
|  |         raise ArgumentError | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     class BinaryTreeNode(object): | ||||||
|  |         def __init__(self, value): | ||||||
|  |             self.value = value | ||||||
|  |             self.left = None | ||||||
|  |             self.right = None | ||||||
|  | 
 | ||||||
|  |         def insert_left(self, value): | ||||||
|  |             self.left = Test.BinaryTreeNode(value) | ||||||
|  |             return self.left | ||||||
|  | 
 | ||||||
|  |         def insert_right(self, value): | ||||||
|  |             self.right = Test.BinaryTreeNode(value) | ||||||
|  |             return self.right | ||||||
|  | 
 | ||||||
|  |     def test_full_tree(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         left = tree.insert_left(30) | ||||||
|  |         right = tree.insert_right(70) | ||||||
|  |         left.insert_left(10) | ||||||
|  |         left.insert_right(40) | ||||||
|  |         right.insert_left(60) | ||||||
|  |         right.insert_right(80) | ||||||
|  |         actual = find_second_largest(tree) | ||||||
|  |         expected = 70 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_largest_has_a_left_child(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         left = tree.insert_left(30) | ||||||
|  |         right = tree.insert_right(70) | ||||||
|  |         left.insert_left(10) | ||||||
|  |         left.insert_right(40) | ||||||
|  |         right.insert_left(60) | ||||||
|  |         actual = find_second_largest(tree) | ||||||
|  |         expected = 60 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_largest_has_a_left_subtree(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         left = tree.insert_left(30) | ||||||
|  |         right = tree.insert_right(70) | ||||||
|  |         left.insert_left(10) | ||||||
|  |         left.insert_right(40) | ||||||
|  |         right_left = right.insert_left(60) | ||||||
|  |         right_left_left = right_left.insert_left(55) | ||||||
|  |         right_left.insert_right(65) | ||||||
|  |         right_left_left.insert_right(58) | ||||||
|  |         actual = find_second_largest(tree) | ||||||
|  |         expected = 65 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_second_largest_is_root_node(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         left = tree.insert_left(30) | ||||||
|  |         tree.insert_right(70) | ||||||
|  |         left.insert_left(10) | ||||||
|  |         left.insert_right(40) | ||||||
|  |         actual = find_second_largest(tree) | ||||||
|  |         expected = 50 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_descending_linked_list(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         left = tree.insert_left(40) | ||||||
|  |         left_left = left.insert_left(30) | ||||||
|  |         left_left_left = left_left.insert_left(20) | ||||||
|  |         left_left_left.insert_left(10) | ||||||
|  |         actual = find_second_largest(tree) | ||||||
|  |         expected = 40 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_ascending_linked_list(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         right = tree.insert_right(60) | ||||||
|  |         right_right = right.insert_right(70) | ||||||
|  |         right_right.insert_right(80) | ||||||
|  |         actual = find_second_largest(tree) | ||||||
|  |         expected = 70 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_error_when_tree_has_one_node(self): | ||||||
|  |         tree = Test.BinaryTreeNode(50) | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             find_second_largest(tree) | ||||||
|  | 
 | ||||||
|  |     def test_error_when_tree_is_empty(self): | ||||||
|  |         with self.assertRaises(Exception): | ||||||
|  |             find_second_largest(None) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
|  | @ -0,0 +1,94 @@ | ||||||
|  | from heapq import heappush, heappop | ||||||
|  | from collections import deque | ||||||
|  | from fixtures import weighted_graph, expanded_weights_graph | ||||||
|  | 
 | ||||||
|  | # UnweightedGraph(a) :: Map(a, Set(a)) | ||||||
|  | # WeightedGraph(a) :: Map(a, Set(a)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # shortest_path_dijkstra :: Vertex -> Vertex -> WeightedGraph(Vertex) | ||||||
|  | def shortest_path_dijkstra(a, b, g): | ||||||
|  |     q = [] | ||||||
|  |     seen = set() | ||||||
|  | 
 | ||||||
|  |     heappush(q, (0, a, [a])) | ||||||
|  | 
 | ||||||
|  |     while q: | ||||||
|  |         w0, v0, path = heappop(q) | ||||||
|  |         if v0 in seen: | ||||||
|  |             continue | ||||||
|  |         elif v0 == b: | ||||||
|  |             return w0, path | ||||||
|  |         for w1, v1 in g.get(v0): | ||||||
|  |             heappush(q, (w0 + w1, v1, path + [v1])) | ||||||
|  |         seen.add(v0) | ||||||
|  |     return 'weighted', 'pizza' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # expand_edge :: Vertex -> (Weight, Vertex) -> Map(Vertex, [Vertex]) | ||||||
|  | def expand_edge(v0, wv): | ||||||
|  |     w, v1 = wv | ||||||
|  |     assert w > 1 | ||||||
|  | 
 | ||||||
|  |     result = {v0: ['{}-{}'.format(v1, 1)]} | ||||||
|  |     for x in range(w - 2): | ||||||
|  |         result['{}-{}'.format(v1, x + 1)] = ['{}-{}'.format(v1, x + 2)] | ||||||
|  |     result['{}-{}'.format(v1, w - 1)] = [v1] | ||||||
|  | 
 | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # expand_weights :: Vertex -> WeightedGraph(Vertex) -> UnweightedGraph(Vertex) | ||||||
|  | def expand_weights(v, g): | ||||||
|  |     result = {} | ||||||
|  |     q = deque() | ||||||
|  |     seen = set() | ||||||
|  | 
 | ||||||
|  |     q.append(v) | ||||||
|  |     while q: | ||||||
|  |         v = d.popleft() | ||||||
|  |         if v in seen: | ||||||
|  |             continue | ||||||
|  |         x = expand_edge(v, g.get) | ||||||
|  |         for w, v1 in g.get(v): | ||||||
|  |             if w > 1: | ||||||
|  |                 ws = expand_edge(v, (w, v1)) | ||||||
|  |                 result = {**result, **ws} | ||||||
|  |             q.append(v) | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # shortest_path_inject :: Vertex -> Vertex -> WeightedGraph(Vertex) | ||||||
|  | def shortest_path_inject(a, b, g): | ||||||
|  |     q = deque() | ||||||
|  |     seen = set() | ||||||
|  | 
 | ||||||
|  |     q.append((a, [a])) | ||||||
|  | 
 | ||||||
|  |     while q: | ||||||
|  |         v0, path = q.popleft() | ||||||
|  |         if v0 == 'dummy': | ||||||
|  |             continue | ||||||
|  |         elif v0 in seen: | ||||||
|  |             continue | ||||||
|  |         elif v0 == b: | ||||||
|  |             return len(path), path | ||||||
|  |         for _, v1 in g.get(v0): | ||||||
|  |             q.append((v1, path + [v1])) | ||||||
|  |         seen.add(v0) | ||||||
|  |         continue | ||||||
|  | 
 | ||||||
|  |     return None, None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | print(expand_edge('a', (4, 'b'))) | ||||||
|  | print(expand_edge('a', (5, 'e'))) | ||||||
|  | assert expand_weights('a', weighted_graph) == expanded_weights_graph | ||||||
|  | # a = 'a' | ||||||
|  | # b = 'd' | ||||||
|  | # w, x = shortest_path_dijkstra(a, b, weighted_graph) | ||||||
|  | # w1, x1 = shortest_path_inject(a, b, weighted_graph) | ||||||
|  | # print("[dijkstra]  Shortest path from {} to {} is {} with weight {}".format( | ||||||
|  | #     a, b, x, w)) | ||||||
|  | # print("[injection] Shortest path from {} to {} is {} with weight {}".format( | ||||||
|  | #     a, b, x1, w1)) | ||||||
							
								
								
									
										34
									
								
								universe/data_structures_and_algorithms/shuffle.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								universe/data_structures_and_algorithms/shuffle.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | ||||||
|  | import random | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def get_random(floor, ceiling): | ||||||
|  |     return random.randrange(floor, ceiling + 1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # shuffle_in_place :: [a] -> IO () | ||||||
|  | def shuffle_in_place(xs): | ||||||
|  |     """Fisher-Yates algorithm. Notice that shuffling here is the same as | ||||||
|  |     selecting a random permutation of the input set, `xs`.""" | ||||||
|  |     n = len(xs) - 1 | ||||||
|  |     for i in range(len(xs)): | ||||||
|  |         r = get_random(i, n) | ||||||
|  |         xs[i], xs[r] = xs[r], xs[i] | ||||||
|  |     return xs | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # shuffle :: [a] -> [a] | ||||||
|  | def shuffle_not_in_place(xs): | ||||||
|  |     result = [] | ||||||
|  | 
 | ||||||
|  |     while xs: | ||||||
|  |         i = get_random(0, len(xs) - 1) | ||||||
|  |         x = xs.pop(i) | ||||||
|  |         result.append(x) | ||||||
|  | 
 | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | xs = [x for x in range(9)] | ||||||
|  | print(xs) | ||||||
|  | # print(shuffle_not_in_place(xs)) | ||||||
|  | print(shuffle_in_place(xs[:])) | ||||||
							
								
								
									
										22
									
								
								universe/data_structures_and_algorithms/string-reverse.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								universe/data_structures_and_algorithms/string-reverse.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | 
 | ||||||
|  | # swap :: Int -> Int -> [Char] -> IO () | ||||||
|  | def swap(ia, iz, xs): | ||||||
|  |     # handle swap when ia == iz | ||||||
|  |     assert ia <= iz | ||||||
|  |     xs[ia], xs[iz] = xs[iz], xs[ia] | ||||||
|  |      | ||||||
|  | 
 | ||||||
|  | # reverse :: [Char] -> IO () | ||||||
|  | def reverse(xs): | ||||||
|  |     ia = 0 | ||||||
|  |     iz = len(xs) - 1 | ||||||
|  | 
 | ||||||
|  |     while ia <= iz: | ||||||
|  |         swap(ia, iz, xs) | ||||||
|  |         ia += 1 | ||||||
|  |         iz -= 1 | ||||||
|  | 
 | ||||||
|  | x = list("superduperpooper") | ||||||
|  | reverse(x) | ||||||
|  | print(x) | ||||||
|  | print("Tests pass") | ||||||
|  | @ -0,0 +1,84 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | class TempTracker(object): | ||||||
|  |     def __init__(self): | ||||||
|  |         # min / max | ||||||
|  |         self.min = None | ||||||
|  |         self.max = None | ||||||
|  |         # mean | ||||||
|  |         self.sum = 0 | ||||||
|  |         self.num = 0 | ||||||
|  |         # mode | ||||||
|  |         self.nums = [0] * 111 | ||||||
|  |         self.mode_num = 0 | ||||||
|  |         self.mode = None | ||||||
|  | 
 | ||||||
|  |     def insert(self, x): | ||||||
|  |         # min / max | ||||||
|  |         if not self.min or x < self.min: | ||||||
|  |             self.min = x | ||||||
|  |         if not self.max or x > self.max: | ||||||
|  |             self.max = x | ||||||
|  |         # mean | ||||||
|  |         self.sum += x | ||||||
|  |         self.num += 1 | ||||||
|  |         # mode | ||||||
|  |         self.nums[x] += 1 | ||||||
|  |         if self.nums[x] >= self.mode_num: | ||||||
|  |             self.mode_num = self.nums[x] | ||||||
|  |             self.mode = x | ||||||
|  | 
 | ||||||
|  |     def get_max(self): | ||||||
|  |         return self.max | ||||||
|  | 
 | ||||||
|  |     def get_min(self): | ||||||
|  |         return self.min | ||||||
|  | 
 | ||||||
|  |     def get_mean(self): | ||||||
|  |         return self.sum / self.num | ||||||
|  | 
 | ||||||
|  |     def get_mode(self): | ||||||
|  |         return self.mode | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Tests | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_tracker_usage(self): | ||||||
|  |         tracker = TempTracker() | ||||||
|  | 
 | ||||||
|  |         tracker.insert(50) | ||||||
|  |         msg = 'failed on first temp recorded' | ||||||
|  |         self.assertEqual(tracker.get_max(), 50, msg='max ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_min(), 50, msg='min ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_mean(), 50.0, msg='mean ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_mode(), 50, msg='mode ' + msg) | ||||||
|  | 
 | ||||||
|  |         tracker.insert(80) | ||||||
|  |         msg = 'failed on higher temp recorded' | ||||||
|  |         self.assertEqual(tracker.get_max(), 80, msg='max ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_min(), 50, msg='min ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_mean(), 65.0, msg='mean ' + msg) | ||||||
|  |         self.assertIn(tracker.get_mode(), [50, 80], msg='mode ' + msg) | ||||||
|  | 
 | ||||||
|  |         tracker.insert(80) | ||||||
|  |         msg = 'failed on third temp recorded' | ||||||
|  |         self.assertEqual(tracker.get_max(), 80, msg='max ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_min(), 50, msg='min ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_mean(), 70.0, msg='mean ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_mode(), 80, msg='mode ' + msg) | ||||||
|  | 
 | ||||||
|  |         tracker.insert(30) | ||||||
|  |         msg = 'failed on lower temp recorded' | ||||||
|  |         self.assertEqual(tracker.get_max(), 80, msg='max ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_min(), 30, msg='min ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_mean(), 60.0, msg='mean ' + msg) | ||||||
|  |         self.assertEqual(tracker.get_mode(), 80, msg='mode ' + msg) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										1
									
								
								universe/data_structures_and_algorithms/test.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								universe/data_structures_and_algorithms/test.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | hello | ||||||
							
								
								
									
										25
									
								
								universe/data_structures_and_algorithms/top-scores.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								universe/data_structures_and_algorithms/top-scores.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | from collections import deque | ||||||
|  | 
 | ||||||
|  | # list: | ||||||
|  | # array: | ||||||
|  | # vector: | ||||||
|  | # bit-{array,vector}: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def sort(xs, highest): | ||||||
|  |     v = [0] * (highest + 1) | ||||||
|  |     result = deque() | ||||||
|  | 
 | ||||||
|  |     for x in xs: | ||||||
|  |         v[x] += 1 | ||||||
|  | 
 | ||||||
|  |     for i, x in enumerate(v): | ||||||
|  |         if x > 0: | ||||||
|  |             result.appendleft(i) | ||||||
|  | 
 | ||||||
|  |     return list(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | assert sort([37, 89, 41, 100, 65, 91, 53], | ||||||
|  |             100) == [100, 91, 89, 65, 53, 41, 37] | ||||||
|  | print("Tests pass!") | ||||||
							
								
								
									
										31
									
								
								universe/data_structures_and_algorithms/topo-sort.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								universe/data_structures_and_algorithms/topo-sort.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | from fixtures import unweighted_digraph | ||||||
|  | from collections import deque | ||||||
|  | 
 | ||||||
|  | # vertices_no_in_edges :: UnweightedDigraph -> Set(Vertex) | ||||||
|  | def vertices_no_in_edges(g): | ||||||
|  |     """Return the vertices in graph `g` with no in-edges.""" | ||||||
|  |     result = set() | ||||||
|  |     vertices = set(g.keys()) | ||||||
|  |     for neighbors in g.values(): | ||||||
|  |         result = result.union(neighbors) | ||||||
|  |     return vertices ^ result | ||||||
|  | 
 | ||||||
|  | # topo_sort :: UnweightedDigraph -> List(Vertex) | ||||||
|  | def topo_sort(g): | ||||||
|  |     q = deque() | ||||||
|  |     seen = set() | ||||||
|  |     result = [] | ||||||
|  |     for x in vertices_no_in_edges(g): | ||||||
|  |         q.append(x) | ||||||
|  |     while q: | ||||||
|  |         vertex = q.popleft() | ||||||
|  |         if vertex in seen: | ||||||
|  |             continue | ||||||
|  |         result.append(vertex) | ||||||
|  |         neighbors = g.get(vertex) | ||||||
|  |         for x in g.get(vertex): | ||||||
|  |             q.append(x) | ||||||
|  |         seen.add(vertex) | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | print(topo_sort(unweighted_digraph)) | ||||||
							
								
								
									
										38
									
								
								universe/data_structures_and_algorithms/trickling-water.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								universe/data_structures_and_algorithms/trickling-water.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | ||||||
|  | class Node(object): | ||||||
|  |     def __init__(self, value, children=[]): | ||||||
|  |         self.value = value | ||||||
|  |         self.children = children | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | def trip_time(node): | ||||||
|  |     s = [] | ||||||
|  |     result = 0 | ||||||
|  |     s.append((node.value, node)) | ||||||
|  |     while s: | ||||||
|  |         p, node = s.pop() | ||||||
|  |         if not node.children: | ||||||
|  |             result = max(result, p) | ||||||
|  |         for x in node.children: | ||||||
|  |             s.append((p + x.value, x)) | ||||||
|  |     return result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | tree = Node( | ||||||
|  |     0, | ||||||
|  |     children=[ | ||||||
|  |         Node(5, children=[Node(6)]), | ||||||
|  |         Node(2, children=[ | ||||||
|  |             Node(6), | ||||||
|  |             Node(10), | ||||||
|  |         ]), | ||||||
|  |         Node(3, children=[Node(2, children=[Node(11)])]), | ||||||
|  |     ]) | ||||||
|  | 
 | ||||||
|  | assert trip_time(tree) == 16 | ||||||
|  | print("Tests pass!") | ||||||
|  | @ -0,0 +1,33 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Solution | ||||||
|  | ################################################################################ | ||||||
|  | # find_repeat :: [Int] -> Int | ||||||
|  | def find_repeat(xs): | ||||||
|  |     n = len(xs) - 1 | ||||||
|  |     return sum(xs) - ((n**2 + n) / 2) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ################################################################################ | ||||||
|  | # Tests | ||||||
|  | ################################################################################ | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     def test_short_list(self): | ||||||
|  |         actual = find_repeat([1, 2, 1]) | ||||||
|  |         expected = 1 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_medium_list(self): | ||||||
|  |         actual = find_repeat([4, 1, 3, 4, 2]) | ||||||
|  |         expected = 4 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  |     def test_long_list(self): | ||||||
|  |         actual = find_repeat([1, 5, 9, 7, 2, 6, 3, 8, 2, 4]) | ||||||
|  |         expected = 2 | ||||||
|  |         self.assertEqual(actual, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
							
								
								
									
										123
									
								
								universe/deepmind/balanced-binary-tree.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								universe/deepmind/balanced-binary-tree.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,123 @@ | ||||||
|  | import unittest | ||||||
|  | from collections import deque | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def is_balanced(node): | ||||||
|  |     q, seen, ds = deque(), set(), set() | ||||||
|  |     q.append((0, node)) | ||||||
|  |     while q: | ||||||
|  |         d, node = q.popleft() | ||||||
|  |         l, r = node.left, node.right | ||||||
|  |         seen.add(node) | ||||||
|  |         if not l and not r: | ||||||
|  |             if d not in ds and len(ds) == 2: | ||||||
|  |                 return False | ||||||
|  |             else: | ||||||
|  |                 ds.add(d) | ||||||
|  |         if l and l not in seen: | ||||||
|  |             q.append((d + 1, l)) | ||||||
|  |         if r and r not in seen: | ||||||
|  |             q.append((d + 1, r)) | ||||||
|  |     return max(ds) - min(ds) <= 1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Tests | ||||||
|  | class Test(unittest.TestCase): | ||||||
|  |     class BinaryTreeNode(object): | ||||||
|  |         def __init__(self, value): | ||||||
|  |             self.value = value | ||||||
|  |             self.left = None | ||||||
|  |             self.right = None | ||||||
|  | 
 | ||||||
|  |         def insert_left(self, value): | ||||||
|  |             self.left = Test.BinaryTreeNode(value) | ||||||
|  |             return self.left | ||||||
|  | 
 | ||||||
|  |         def insert_right(self, value): | ||||||
|  |             self.right = Test.BinaryTreeNode(value) | ||||||
|  |             return self.right | ||||||
|  | 
 | ||||||
|  |     def test_full_tree(self): | ||||||
|  |         tree = Test.BinaryTreeNode(5) | ||||||
|  |         left = tree.insert_left(8) | ||||||
|  |         right = tree.insert_right(6) | ||||||
|  |         left.insert_left(1) | ||||||
|  |         left.insert_right(2) | ||||||
|  |         right.insert_left(3) | ||||||
|  |         right.insert_right(4) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_both_leaves_at_the_same_depth(self): | ||||||
|  |         tree = Test.BinaryTreeNode(3) | ||||||
|  |         left = tree.insert_left(4) | ||||||
|  |         right = tree.insert_right(2) | ||||||
|  |         left.insert_left(1) | ||||||
|  |         right.insert_right(9) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_leaf_heights_differ_by_one(self): | ||||||
|  |         tree = Test.BinaryTreeNode(6) | ||||||
|  |         left = tree.insert_left(1) | ||||||
|  |         right = tree.insert_right(0) | ||||||
|  |         right.insert_right(7) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_leaf_heights_differ_by_two(self): | ||||||
|  |         tree = Test.BinaryTreeNode(6) | ||||||
|  |         left = tree.insert_left(1) | ||||||
|  |         right = tree.insert_right(0) | ||||||
|  |         right_right = right.insert_right(7) | ||||||
|  |         right_right.insert_right(8) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_three_leaves_total(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         left = tree.insert_left(5) | ||||||
|  |         right = tree.insert_right(9) | ||||||
|  |         right.insert_left(8) | ||||||
|  |         right.insert_right(5) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_both_subtrees_superbalanced(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         left = tree.insert_left(5) | ||||||
|  |         right = tree.insert_right(9) | ||||||
|  |         right_left = right.insert_left(8) | ||||||
|  |         right.insert_right(5) | ||||||
|  |         right_left.insert_left(7) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_both_subtrees_superbalanced_two(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         left = tree.insert_left(2) | ||||||
|  |         right = tree.insert_right(4) | ||||||
|  |         left.insert_left(3) | ||||||
|  |         left_right = left.insert_right(7) | ||||||
|  |         left_right.insert_right(8) | ||||||
|  |         right_right = right.insert_right(5) | ||||||
|  |         right_right_right = right_right.insert_right(6) | ||||||
|  |         right_right_right.insert_right(9) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertFalse(result) | ||||||
|  | 
 | ||||||
|  |     def test_only_one_node(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  |     def test_linked_list_tree(self): | ||||||
|  |         tree = Test.BinaryTreeNode(1) | ||||||
|  |         right = tree.insert_right(2) | ||||||
|  |         right_right = right.insert_right(3) | ||||||
|  |         right_right.insert_right(4) | ||||||
|  |         result = is_balanced(tree) | ||||||
|  |         self.assertTrue(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unittest.main(verbosity=2) | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue