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