diff options
Diffstat (limited to 'universe')
131 files changed, 13792 insertions, 0 deletions
diff --git a/universe/.envrc b/universe/.envrc new file mode 100644 index 000000000000..365d37dbcabf --- /dev/null +++ b/universe/.envrc @@ -0,0 +1 @@ +export NIX_PATH=nixpkgs=$HOME/.nix-defexpr/nixpkgs:depot=$HOME/depot:universe=$HOME/universe diff --git a/universe/.gitignore b/universe/.gitignore new file mode 100644 index 000000000000..12ecf28fd42f --- /dev/null +++ b/universe/.gitignore @@ -0,0 +1,7 @@ +# Haskell +*.hi +*.o + +# Python +__pycache__ +*.class diff --git a/universe/README.md b/universe/README.md new file mode 100644 index 000000000000..4fa248554751 --- /dev/null +++ b/universe/README.md @@ -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. diff --git a/universe/ac_types/ac_types.py b/universe/ac_types/ac_types.py new file mode 100644 index 000000000000..8a3513c59798 --- /dev/null +++ b/universe/ac_types/ac_types.py @@ -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') diff --git a/universe/ac_types/attributes.pb b/universe/ac_types/attributes.pb new file mode 100644 index 000000000000..6b893647c7ba --- /dev/null +++ b/universe/ac_types/attributes.pb @@ -0,0 +1,1014 @@ +configs { + # System Attribute - Category + id: "^^1" + type: STRING + access_type: READ_WRITE +} +configs { + # System Attribute - Product + id: "^^11" + type: STRING + access_type: READ +} +configs { + # System Attribute - Customer Email + id: "^^3" + type: STRING + access_type: READ +} +configs { + # System Attribute - Adwords Customer ID + id: "^^30" + type: STRING + access_type: READ +} +configs { + # System Attribute - Customer Gaia ID + id: "^^4" + type: INT + access_type: READ +} +configs { + # System Attribute - Customer Name + id: "^^5" + type: STRING + access_type: READ +} +configs { + # System Attribute - Locale + id: "^^9" + type: STRING + access_type: READ +} +configs { + # System Attribute - Case Source + id: "^1006" + type: STRING + access_type: READ +} +configs { + # AR.Claim.KnowledgeTestClaimId + id: "^106464" + type: STRING + access_type: READ +} +configs { + # AR.Claim.IssueType + id: "^106466" + type: ENUM + access_type: READ +} +configs { + # AR.Claim.StrongAuthPhone + id: "^106474" + type: STRING + access_type: READ +} +configs { + # AR.Claim.IsStopMessageReceived + id: "^106482" + type: BOOL + access_type: READ +} +configs { + # Atlas.CustomerId + id: "^1421" + type: STRING + access_type: READ +} +configs { + # Form.form-id + id: "^4297" + type: STRING + access_type: READ +} +configs { + # Form.Ar_descr_textbox + id: "^6664" + type: STRING + access_type: READ +} +configs { + # AR.ART.LastDecisionRecommended + id: "^106918" + type: ENUM + access_type: WRITE +} +configs { + # AR.ART.LastDecisionTaken + id: "^106919" + type: ENUM + access_type: WRITE +} +configs { + # AR.ART.LastDecisionTime + id: "^106920" + type: STRING + access_type: WRITE +} +configs { + # AR.ART.LastDecisionType + id: "^106921" + type: ENUM + access_type: WRITE +} +configs { + # System Attribute - Resolution ID + id: "^22020" + type: STRING + access_type: WRITE +} +configs { + # b/134163005 - frd.quality_pre_check_state.core + id: "^83347" + type: STRING + access_type: READ_WRITE +} +configs { + # b/134168536 - PRIORITY + id: "^^10" + type: INT + access_type: READ +} +configs { + # b/134169885 - STO_InternalCategory + id: "^3618" + type: STRING + access_type: READ_WRITE +} +configs { + # b/134408264 - neo.notification_requested + id: "^84074" + type: STRING + access_type: READ_WRITE +} +configs { + # b/135613797 - Form.media_plan_ID + id: "^75926" + type: STRING + access_type: READ +} +configs { + # b/135613797 - Form.help_needed + id: "^80735" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.company_id + id: "^14773" + type: INT + access_type: READ +} +configs { + # b/135614853 - sales_task.TeamName + id: "^16367" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.VerticalName + id: "^16368" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.RegionName + id: "^16369" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.Sector + id: "^16372" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.SubSector + id: "^16373" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.parent_company_id + id: "^25555" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.service_channel + id: "^25557" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.company_name + id: "^26743" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.global_parent_company_id + id: "^29339" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.global_parent_company_name + id: "^29340" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.parent_company_name + id: "^29341" + type: STRING + access_type: READ +} +configs { + # b/135614853 - CaseCreator.Email + id: "^5709" + type: STRING + access_type: READ +} +configs { + # b/135614853 - neo.product + id: "^68613" + type: STRING + access_type: READ +} +configs { + # b/135614853 - neo.request_type + id: "^68616" + type: STRING + access_type: READ +} +configs { + # b/135614853 - neo.workdriver + id: "^68617" + type: STRING + access_type: READ +} +configs { + # b/135614853 - neo.campaign_type + id: "^68621" + type: STRING + access_type: READ +} +configs { + # b/135614853 - neo.market + id: "^69794" + type: STRING + access_type: READ +} +configs { + # b/135614853 - neo.interaction_type + id: "^69795" + type: STRING + access_type: READ +} +configs { + # b/135614853 - sales_task.chutney_key + id: "^70302" + type: STRING + access_type: READ +} +configs { + # b/135614853 - frd.case_source.core + id: "^76697" + type: STRING + access_type: READ +} +configs { + # b/135614853 - frd.google_ads_internal_customer_id.target + id: "^77629" + type: INT + access_type: READ +} +configs { + # b/135614853 - frd.google_ads_internal_customer_id.target_candidate + id: "^77634" + type: INT + access_type: READ +} +configs { + # b/135614853 - neo.Sales_Region + id: "^81664" + type: STRING + access_type: READ +} +configs { + # b/135614853 - neo.source_mechanism + id: "^82204" + type: STRING + access_type: READ +} +configs { + # b/135614853 - neo.vertical + id: "^82589" + type: STRING + access_type: READ +} +configs { + # b/135943348 - IS_CONSULT + id: "^1224" + type: BOOL + access_type: READ +} +configs { + # b/135943691 - Form.ic_sto_priority + id: "^6078" + type: STRING + access_type: READ +} +configs { + # b/135943174 - neo.program + id: "^69798" + type: STRING + access_type: READ +} +configs { + # b/135942978 - Form.what_is_your_question_about + id: "^14511" + type: STRING + access_type: READ +} +configs { + # b/135942692 - sales_task.gt_priority + id: "^25484" + type: STRING + access_type: READ +} +configs { + # b/138591197 - Form.pii_status + id: "^30810" + type: STRING + access_type: READ +} +configs { + # b/138591197 - Form.pii_status - STAGING ATTRIBUTE + id: "^122997" + type: STRING + access_type: READ +} +configs { + # b/138590939 - Form.pii_AWID + id: "^30811" + type: STRING + access_type: READ +} +configs { + # b/138590939 - Form.pii_AWID - STAGING ATTRIBUTE + id: "^122998" + type: STRING + access_type: READ +} +configs { + # b/138590649 - Form.customerId + id: "^1658" + type: STRING + access_type: READ +} +configs { + # b/138592092 - Form.form + id: "^3664" + type: STRING + access_type: READ +} +configs { + # b/138592092 - Form.form - STAGING ATTRIBUTE + id: "^113826" + type: STRING + access_type: READ +} +configs { + # b/138591447 - Form.pubid + id: "^3693" + type: STRING + access_type: READ +} +configs { + # b/138591447 - Form.pubid - STAGING ATTRIBUTE + id: "^102721" + type: STRING + access_type: READ +} +configs { + # b/138590817 - Form.status + id: "^7502" + type: STRING + access_type: READ +} +configs { + # b/138590817 - Form.status - STAGING ATTRIBUTE + id: "^104111" + type: STRING + access_type: READ +} +configs { + # b/138811198 - sales_task.Country + id: "^16371" + type: STRING + access_type: READ +} +configs { + # b/138811198 - sales_task.country_code + id: "^25556" + type: STRING + access_type: READ +} +configs { + # b/138811198 - sales_task.client_business_objective + id: "^85372" + type: STRING + access_type: READ +} +configs { + # b/138811198 - sales_task.timeline_consideration + id: "^85375" + type: STRING + access_type: READ +} +configs { + # b/138843621 - Form.bse_priority_level + id: "^71223" + type: STRING + access_type: READ +} +configs { + # b/139361297 - Form.pub_id + id: "^4302" + type: STRING + access_type: READ +} +configs { + # b/139361297 - Form.admob_pubid + id: "^8472" + type: STRING + access_type: READ +} +configs { + # b/139361297 - Form.upload_document_id + id: "^7067" + type: STRING + access_type: READ +} +configs { + # b/139761352 - sales_task.assigned_to + id: "^73218" + type: STRING + access_type: READ +} +configs { + # b/140811258 - neo.source_context + id: "^83722" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.account_assigned_to_a_PTL + id: "^84587" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.account_id + id: "^2259" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Additional_Advertiser_ID_s + id: "^84575" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.additional_comments + id: "^3501" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.advertiser_name + id: "^4171" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.agency_id + id: "^76244" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Authorized_Buyers_customer_ID + id: "^87627" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.company_id + id: "^3416" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.company_name + id: "^3239" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Country_of_Support + id: "^84581" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Customer_Contact_Email + id: "^84577" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.customer_contact_name + id: "^7553" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Greentea_ID + id: "^86872" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.impact + id: "^68442" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.impact_type + id: "^16645" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Link_Partner_s + id: "^84589" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.network_id + id: "^2939" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Notification_Email + id: "^84582" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.partner_agency_name + id: "^86962" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.partner_id + id: "^2342" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Primary_Advertiser_ID_1 + id: "^86874" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Primary_Advertiser_ID + id: "^84591" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Primary_Product + id: "^84588" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Request_Type + id: "^4833" + type: STRING + access_type: READ +} +configs { + # b/141865219 - Form.Request_Sub_Type + id: "^84831" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Rquest_Sub_Type + id: "^84593" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.sector + id: "^18760" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.sub_sector + id: "^87768" + type: STRING + access_type: READ +} +configs { + # b/141027066 - Form.Work_doesnt_affect_Advertiser + id: "^84565" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.support_need + id: "^60520" + type: ENUM + access_type: READ +} +configs { + # b/142882516 - sales_task.campaign_name + id: "^25128" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.net_campaign_budget + id: "^25118" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.currency + id: "^25115" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.advertiser_market + id: "^81059" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.advertiser_market_cs + id: "^81091" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.country_cs + id: "^81090" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.campaign_start_date + id: "^25108" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.campaign_end_date + id: "^25114" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.potential_date + id: "^80985" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.date_flexible + id: "^80991" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.agency + id: "^25116" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.sales_poc + id: "^61764" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.external_poc + id: "^62583" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.dvip_status + id: "^80988" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.campaign_supported + id: "^81060" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.ad_platform + id: "^80983" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.video_format + id: "^80992" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.launched_before + id: "^80959" + type: STRING + access_type: READ +} +configs { + # b/142882516 - sales_task.request_details + id: "^25126" + type: STRING + access_type: READ +} +configs { + # b/142882516 - neo.component + id: "^68623" + type: STRING + access_type: READ +} +configs { + # b/142882516 - neoenum.gps_project_type + id: "^88941" + type: STRING + access_type: READ +} +configs { + # b/143390406 - neoenum.youtube_program_pitched + id: "^88894" + type: STRING + access_type: READ_WRITE +} +configs { + # b/143892899 - neo.organization + id: "^69793" + type: STRING + access_type: READ +} +configs { + # b/145022355 - LR_isFlagged + id: "^6736" + type: BOOL + access_type: READ +} +configs { + # b/145022355 - legal_AbuseCategory + id: "^3614" + type: ENUM + access_type: READ +} +configs { + # b/145022355 - legal_HiddenProduct + id: "^3615" + type: ENUM + access_type: READ +} +configs { + # b/145022355 - CountryIsoAlpha2 + id: "^3497" + type: ENUM + access_type: READ +} +configs { + # b/145022355 - legal_ChillingId + id: "^3616" + type: STRING + access_type: READ +} +configs { + # b/145022355 - LegalRemovals_SESTA + id: "^71537" + type: BOOL + access_type: READ +} +configs { + # b/145707965 - sales_task.accounts_field2 + id: "^80051" + type: STRING + access_type: READ +} +configs { + # b/145707965 - sales_task.cid_rollup + id: "^80052" + type: STRING + access_type: READ +} +configs { + # b/145707965 - sales_task.proposed_company_id + id: "^62779" + type: STRING + access_type: READ +} +configs { + # b/146005429 - Form.geolocation + id: "^3328" + type: STRING + access_type: READ +} +configs { + # b/146066105 - Form.url_box3 + id: "^5653" + type: STRING + access_type: READ +} +configs { + # b/146066105 - Form.full_name + id: "^2981" + type: STRING + access_type: READ +} +configs { + # b/146066105 - Form.signature + id: "^3483" + type: STRING + access_type: READ +} +configs { + # b/146060287 - Form.publisher_code + id: "^86622" + type: STRING + access_type: READ +} +configs { + # b/146445306 - neoenum.consult_type + id: "^88149" + type: STRING + access_type: READ +} +configs { + # b/146445306 - neoenum.program + id: "^87379" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.ad_language + id: "^3906" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.summary_of_issue + id: "^5305" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Atlas.AbuseTagIds + id: "^2356" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.website_req + id: "^1677" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.who_pays + id: "^14657" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.sample_keywords + id: "^1881" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.billing_address_street + id: "^1880" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.billing_address_town + id: "^1879" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.billing_address_zip + id: "^1885" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.billing_country_req + id: "^1886" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.business_desc + id: "^14660" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.owner_or_emp + id: "^14661" + type: STRING + access_type: READ +} +configs { + # b/146445306 - Form.payment_option + id: "^1882" + type: STRING + access_type: READ +} +configs { + # b/146445306 - ChatForm.phone_number + id: "^30248" + type: STRING + access_type: READ +} +configs { + # b/146445306 - pool + id: "^^35" + type: STRING + access_type: READ +} +configs { + # b/146649910 - neoenum.product + id: "^87422" + type: STRING + access_type: READ +} +configs { + # b/146347546 - sales_task.requested_quarter + id: "^77809" + type: STRING + access_type: READ +} +configs { + # b/146347546 - sales_task.proposed_company_name + id: "^62778" + type: STRING + access_type: READ +} +configs { + # b/146347546 - sales_task.company_name_pdc_copy + id: "^80083" + type: STRING + access_type: READ +} +configs { + # b/146347546 - sales_task.company_currently_exists + id: "^75285" + type: STRING + access_type: READ +} +configs { + # b/146347546 - sales_task.parent_or_division + id: "^76645" + type: STRING + access_type: READ +} +configs { + # b/146347546 - sales_task.proposed_company_website + id: "^76644" + type: STRING + access_type: READ +} \ No newline at end of file diff --git a/universe/ac_types/dict.py b/universe/ac_types/dict.py new file mode 100644 index 000000000000..9975ec8a970f --- /dev/null +++ b/universe/ac_types/dict.py @@ -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 diff --git a/universe/ac_types/example.textproto b/universe/ac_types/example.textproto new file mode 100644 index 000000000000..987c45f78f7e --- /dev/null +++ b/universe/ac_types/example.textproto @@ -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" + } +} \ No newline at end of file diff --git a/universe/ac_types/expected/EU Election Certification (Non-Olympus).textproto b/universe/ac_types/expected/EU Election Certification (Non-Olympus).textproto new file mode 100644 index 000000000000..5ebfebb6783d --- /dev/null +++ b/universe/ac_types/expected/EU Election Certification (Non-Olympus).textproto @@ -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 + } +} diff --git a/universe/ac_types/expected/EU Election Certification (Olympus).textproto b/universe/ac_types/expected/EU Election Certification (Olympus).textproto new file mode 100644 index 000000000000..8650de0d5e88 --- /dev/null +++ b/universe/ac_types/expected/EU Election Certification (Olympus).textproto @@ -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 + } +} \ No newline at end of file diff --git a/universe/ac_types/expected/Non-Sensitive Ads Review (Olympus).textproto b/universe/ac_types/expected/Non-Sensitive Ads Review (Olympus).textproto new file mode 100644 index 000000000000..5ff397946bec --- /dev/null +++ b/universe/ac_types/expected/Non-Sensitive Ads Review (Olympus).textproto @@ -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" + } +} \ No newline at end of file diff --git a/universe/ac_types/f.py b/universe/ac_types/f.py new file mode 100644 index 000000000000..44dcf0d32bf7 --- /dev/null +++ b/universe/ac_types/f.py @@ -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 diff --git a/universe/ac_types/fs.py b/universe/ac_types/fs.py new file mode 100644 index 000000000000..68b34b64bffe --- /dev/null +++ b/universe/ac_types/fs.py @@ -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)) diff --git a/universe/ac_types/input_constant.py b/universe/ac_types/input_constant.py new file mode 100644 index 000000000000..9b32a3b493be --- /dev/null +++ b/universe/ac_types/input_constant.py @@ -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') diff --git a/universe/ac_types/log.py b/universe/ac_types/log.py new file mode 100644 index 000000000000..797e2f719942 --- /dev/null +++ b/universe/ac_types/log.py @@ -0,0 +1,9 @@ +enable = False + + +def warn(x): + """Print `x` as a warning.""" + if enable: + print('[Warning]: {}'.format(x)) + else: + return None diff --git a/universe/ac_types/notes_with_jason.txt b/universe/ac_types/notes_with_jason.txt new file mode 100644 index 000000000000..5c2972ee579f --- /dev/null +++ b/universe/ac_types/notes_with_jason.txt @@ -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: ??? + }) +} diff --git a/universe/ac_types/output.textproto b/universe/ac_types/output.textproto new file mode 100644 index 000000000000..de2cce77ca71 --- /dev/null +++ b/universe/ac_types/output.textproto @@ -0,0 +1,4483 @@ +# File: "Non-Sensitive Ads Review (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_Adsreview" + max_wait_time_for_consult_secs: 43200 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^1421" + } + 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 + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^1421" + } + 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" + } +} +# File: "Ads Review (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_Adsreview" + max_wait_time_for_consult_secs: 43200 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^1421" + } + inputs { + type: PARENT_FRD + consult_frd_id: "ISSUE_DESCRIPTION" + is_optional: false + parent_frd_id: "^5305" + } + 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 + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^1421" + } + inputs { + type: PARENT_FRD + consult_frd_id: "ISSUE_DESCRIPTION" + is_optional: false + parent_frd_id: "^5305" + } + 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" + } +} +# File: "Ad Review (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_Adsreview" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ad_review_request_autoconsult" + } + + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + 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: "^1421" + } + inputs { + type: PARENT_FRD + consult_frd_id: "ISSUE_DESCRIPTION" + is_optional: false + parent_frd_id: "^5305" + } + 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" + } +} +# File: "Review Account Under Review (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" + is_optional: false + signal_type: "^1421" + } + 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: "review_account_under_review" + } + inputs { + type: CONSTANT + consult_frd_id: "INDENTIFIER_COMPLEXITY" + is_optional: false + constant_value: "easy" + } + inputs { + type: SIGNAL + consult_frd_id: "POLICY" + is_optional: false + signal_type: "^2356" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" + is_optional: false + signal_type: "^1421" + } + 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: "review_account_under_review" + } + inputs { + type: CONSTANT + consult_frd_id: "INDENTIFIER_COMPLEXITY" + is_optional: false + constant_value: "easy" + } + inputs { + type: SIGNAL + consult_frd_id: "POLICY" + is_optional: false + signal_type: "^2356" + } +} +# File: "Accounts Review Requests (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" + is_optional: false + signal_type: "^1421" + } + inputs { + type: PARENT_FRD + consult_frd_id: "ISSUE_DESCRIPTION" + is_optional: false + parent_frd_id: "^2941" + } + inputs { + type: CONSTANT + consult_frd_id: "INDENTIFIER_COMPLEXITY" + is_optional: false + constant_value: "easy" + } + inputs { + type: SIGNAL + consult_frd_id: "POLICY" + is_optional: false + signal_type: "^2356" + } +} +# File: "Review Suspended Account (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" + is_optional: false + signal_type: "^1421" + } + inputs { + type: CONSTANT + consult_frd_id: "REQUEST" + is_optional: false + constant_value: "rereview_suspended_account" + } + inputs { + type: CONSTANT + consult_frd_id: "INDENTIFIER_COMPLEXITY" + is_optional: false + constant_value: "easy" + } + inputs { + type: SIGNAL + consult_frd_id: "POLICY" + is_optional: false + signal_type: "^2356" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" + is_optional: false + signal_type: "^1421" + } + inputs { + type: CONSTANT + consult_frd_id: "REQUEST" + is_optional: false + constant_value: "rereview_suspended_account" + } + inputs { + type: CONSTANT + consult_frd_id: "INDENTIFIER_COMPLEXITY" + is_optional: false + constant_value: "easy" + } + inputs { + type: SIGNAL + consult_frd_id: "POLICY" + is_optional: false + signal_type: "^2356" + } +} +# File: "Review Suspended Account (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" + is_optional: false + signal_type: "^1421" + } + inputs { + type: CONSTANT + consult_frd_id: "REQUEST" + is_optional: false + constant_value: "rereview_suspended_account" + } + inputs { + type: CONSTANT + consult_frd_id: "INDENTIFIER_COMPLEXITY" + is_optional: false + constant_value: "easy" + } + inputs { + type: SIGNAL + consult_frd_id: "POLICY" + is_optional: false + signal_type: "^2356" + } +} +# File: "Suspended Long Form (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.policy" + is_optional: false + signal_type: "^2356" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.website_req" + is_optional: false + parent_frd_id: "^1677" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.who_pays" + is_optional: false + parent_frd_id: "^14657" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.summary_of_issue" + is_optional: false + parent_frd_id: "^5305" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.sample_keywords" + is_optional: false + parent_frd_id: "^1881" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_street" + is_optional: false + parent_frd_id: "^1880" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_town" + is_optional: false + parent_frd_id: "^1879" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_zip" + is_optional: false + parent_frd_id: "^1885" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_country_req" + is_optional: false + parent_frd_id: "^1886" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.business_desc" + is_optional: false + parent_frd_id: "^14660" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.countries_business_serve" + is_optional: false + parent_frd_id: "^14659" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.name" + is_optional: false + parent_frd_id: "^1665" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.owner_or_emp" + is_optional: false + parent_frd_id: "^14661" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.payment_option" + is_optional: false + parent_frd_id: "^1882" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.phone_number" + is_optional: false + parent_frd_id: "^30248" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.policy" + is_optional: false + signal_type: "^2356" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.website_req" + is_optional: false + parent_frd_id: "^1677" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.who_pays" + is_optional: false + parent_frd_id: "^14657" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.summary_of_issue" + is_optional: false + parent_frd_id: "^5305" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.sample_keywords" + is_optional: false + parent_frd_id: "^1881" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_street" + is_optional: false + parent_frd_id: "^1880" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_town" + is_optional: false + parent_frd_id: "^1879" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_zip" + is_optional: false + parent_frd_id: "^1885" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_country_req" + is_optional: false + parent_frd_id: "^1886" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.business_desc" + is_optional: false + parent_frd_id: "^14660" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.countries_business_serve" + is_optional: false + parent_frd_id: "^14659" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.name" + is_optional: false + parent_frd_id: "^1665" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.owner_or_emp" + is_optional: false + parent_frd_id: "^14661" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.payment_option" + is_optional: false + parent_frd_id: "^1882" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.phone_number" + is_optional: false + parent_frd_id: "^30248" + } +} +# File: "Suspended Long Form (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.policy" + is_optional: false + signal_type: "^2356" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.website_req" + is_optional: false + parent_frd_id: "^1677" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.who_pays" + is_optional: false + parent_frd_id: "^14657" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.summary_of_issue" + is_optional: false + parent_frd_id: "^5305" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.sample_keywords" + is_optional: false + parent_frd_id: "^1881" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_street" + is_optional: false + parent_frd_id: "^1880" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_town" + is_optional: false + parent_frd_id: "^1879" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_zip" + is_optional: false + parent_frd_id: "^1885" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_country_req" + is_optional: false + parent_frd_id: "^1886" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.business_desc" + is_optional: false + parent_frd_id: "^14660" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.countries_business_serve" + is_optional: false + parent_frd_id: "^14659" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.name" + is_optional: false + parent_frd_id: "^1665" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.owner_or_emp" + is_optional: false + parent_frd_id: "^14661" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.payment_option" + is_optional: false + parent_frd_id: "^1882" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.phone_number" + is_optional: false + parent_frd_id: "^30248" + } +} +# File: "Copyright (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + inputs { + type: CONSTANT + consult_frd_id: "REQUEST" + is_optional: false + constant_value: "certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "POLICY" + is_optional: false + constant_value: "copyrights" + } + inputs { + type: CONSTANT + consult_frd_id: "INDENTIFIER_COMPLEXITY" + is_optional: false + constant_value: "easy" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + inputs { + type: CONSTANT + consult_frd_id: "REQUEST" + is_optional: false + constant_value: "certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "POLICY" + is_optional: false + constant_value: "copyrights" + } + inputs { + type: CONSTANT + consult_frd_id: "INDENTIFIER_COMPLEXITY" + is_optional: false + constant_value: "easy" + } +} +# File: "Copyright (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + inputs { + type: CONSTANT + consult_frd_id: "REQUEST" + is_optional: false + constant_value: "certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "POLICY" + is_optional: false + constant_value: "copyrights" + } + inputs { + type: CONSTANT + consult_frd_id: "INDENTIFIER_COMPLEXITY" + is_optional: false + constant_value: "easy" + } +} +# File: "EU Election Certification (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +# File: "EU Election Certification #2 (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "hard" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.product" + is_optional: false + signal_type: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + 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: "hard" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.product" + is_optional: false + signal_type: "^68613" + } +} +# File: "EU Election Certification (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + 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: ""Germany" or "Netherlands" or "Spain" or "Check Republic" or "United Kingdom" # TODO(wpcarro): scrape.attribute_id_for could not find the ID." + } + 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: "^68613" + } +} +# File: "EU Election Certification #2 (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +# File: "US Election Certification (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "elections" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.country" + is_optional: false + constant_value: "united_states_of_america" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "elections" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.country" + is_optional: false + constant_value: "united_states_of_america" + } + 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: "^68613" + } +} +# File: "US Election Certification (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "elections" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.country" + is_optional: false + constant_value: "united_states_of_america" + } + 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: "^68613" + } +} +# File: "IN Election Certification (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.product" + is_optional: false + signal_type: "^68613" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "elections" + } + inputs { + type: CONSTANT + consult_frd_id: "Neo.country" + is_optional: false + constant_value: "india" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.product" + is_optional: false + signal_type: "^68613" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "elections" + } + inputs { + type: CONSTANT + consult_frd_id: "Neo.country" + is_optional: false + constant_value: "india" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } +} +# File: "IN Election Certification (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.product" + is_optional: false + signal_type: "^68613" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "elections" + } + inputs { + type: CONSTANT + consult_frd_id: "Neo.country" + is_optional: false + constant_value: "india" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } +} +# File: "NY Election Certification (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "us_election_ads_new_york_only" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.country" + is_optional: false + constant_value: "united_states_of_america" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.product" + is_optional: false + constant_value: "google_ads" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "us_election_ads_new_york_only" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.country" + is_optional: false + constant_value: "united_states_of_america" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.product" + is_optional: false + constant_value: "google_ads" + } +} +# File: "NY Election Certification (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "us_election_ads_new_york_only" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.country" + is_optional: false + constant_value: "united_states_of_america" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } +} +# File: "Ticket Seller Certification (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "event_ticket_reseller" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "event_ticket_reseller" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "Ticket Seller Certification (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "event_ticket_reseller" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "Pharma Certification EMEA (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "online_pharmacy_certification_required" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + 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: "online_pharmacy_certification_required" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } +} +# File: "Pharma Certification EMEA (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + 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: "online_pharmacy_certification_required" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } +} +# File: "CSFP Certification (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "complex_speculative_financial_policy" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "complex_speculative_financial_policy" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "CSFP Certification (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "complex_speculative_financial_policy" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "Social Casino Games Certification (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "social_casino_games" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "social_casino_games" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "Social Casino Games Certification (NonOlympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "social_casino_games" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "Gambling Certification (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "online_gambling" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "online_gambling" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "Gambling Certification (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "online_gambling" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "Addiction Services Certification (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + 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: "addiction_services" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + 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: "addiction_services" + } +} +# File: "Addiction Services Certification (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + 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: "addiction_services" + } +} +# File: "HTML5 Application (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_HTML5_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.request" + is_optional: false + constant_value: "whitelisting" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.component" + is_optional: false + constant_value: "html5_ads" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_HTML5_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.request" + is_optional: false + constant_value: "whitelisting" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.component" + is_optional: false + constant_value: "html5_ads" + } +} +# File: "HTML5 Application (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_HTML5_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.request" + is_optional: false + constant_value: "whitelisting" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.component" + is_optional: false + constant_value: "html5_ads" + } +} +# File: "Free Desktop Software Policy (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "unwanted_software" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "unwanted_software" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "Free Desktop Software Policy (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 259200 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_certs_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "Certifications" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.policy" + is_optional: false + constant_value: "unwanted_software" + } + inputs { + type: CONSTANT + consult_frd_id: "neo.complexity" + is_optional: false + constant_value: "easy" + } + inputs { + type: PARENT_FRD + consult_frd_id: "neo.country" + is_optional: false + parent_frd_id: "^16296" + } +} +# File: "Untrustworthy Promotions (Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" + is_optional: false + signal_type: "^1421" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.policy" + is_optional: false + signal_type: "^2356" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.website_req" + is_optional: false + parent_frd_id: "^1677" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.who_pays" + is_optional: false + parent_frd_id: "^14657" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.summary_of_issue" + is_optional: false + parent_frd_id: "^5305" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.sample_keywords" + is_optional: false + parent_frd_id: "^1881" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_street" + is_optional: false + parent_frd_id: "^1880" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_town" + is_optional: false + parent_frd_id: "^1879" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_zip" + is_optional: false + parent_frd_id: "^1885" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_country_req" + is_optional: false + parent_frd_id: "^1886" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.business_desc" + is_optional: false + parent_frd_id: "^14660" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.countries_business_serve" + is_optional: false + parent_frd_id: "^14659" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.name" + is_optional: false + parent_frd_id: "^1665" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.owner_or_emp" + is_optional: false + parent_frd_id: "^14661" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.payment_option" + is_optional: false + parent_frd_id: "^1882" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.phone_number" + is_optional: false + parent_frd_id: "^30248" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_lookup_frds { + id: "^87379" + value: "olympus_plus" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "GOOGLE_ADS_INTERNAL_CUSTOMER_ID" + is_optional: false + signal_type: "^1421" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.policy" + is_optional: false + signal_type: "^2356" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.website_req" + is_optional: false + parent_frd_id: "^1677" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.who_pays" + is_optional: false + parent_frd_id: "^14657" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.summary_of_issue" + is_optional: false + parent_frd_id: "^5305" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.sample_keywords" + is_optional: false + parent_frd_id: "^1881" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_street" + is_optional: false + parent_frd_id: "^1880" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_town" + is_optional: false + parent_frd_id: "^1879" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_zip" + is_optional: false + parent_frd_id: "^1885" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_country_req" + is_optional: false + parent_frd_id: "^1886" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.business_desc" + is_optional: false + parent_frd_id: "^14660" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.countries_business_serve" + is_optional: false + parent_frd_id: "^14659" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.name" + is_optional: false + parent_frd_id: "^1665" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.owner_or_emp" + is_optional: false + parent_frd_id: "^14661" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.payment_option" + is_optional: false + parent_frd_id: "^1882" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.phone_number" + is_optional: false + parent_frd_id: "^30248" + } +} +# File: "Untrustworthy Promotions (Non-Olympus)" +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_accountappeals" + max_wait_time_for_consult_secs: 172800 + reroute_on_customer_interaction: true + parent_lookup_frds { + id: "^88149" + value: "ads_accountappeals_autoconsult" + } + parent_return_routing_frds { + id: "ROBOT_WORKFLOW_STATE" + value: "automation_escalate" + } + consult_routing_frds { + id: "ORGANIZATION" + value: "trust_safety" + } + consult_routing_frds { + id: "VERTICAL" + value: "ads" + } + consult_routing_frds { + id: "REQUEST_TYPE" + value: "accounts_review" + } + inputs { + type: SIGNAL + consult_frd_id: "neo.policy" + is_optional: false + signal_type: "^2356" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.website_req" + is_optional: false + parent_frd_id: "^1677" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.who_pays" + is_optional: false + parent_frd_id: "^14657" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.summary_of_issue" + is_optional: false + parent_frd_id: "^5305" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.sample_keywords" + is_optional: false + parent_frd_id: "^1881" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_street" + is_optional: false + parent_frd_id: "^1880" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_town" + is_optional: false + parent_frd_id: "^1879" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_address_zip" + is_optional: false + parent_frd_id: "^1885" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.billing_country_req" + is_optional: false + parent_frd_id: "^1886" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.business_desc" + is_optional: false + parent_frd_id: "^14660" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.countries_business_serve" + is_optional: false + parent_frd_id: "^14659" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.name" + is_optional: false + parent_frd_id: "^1665" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.owner_or_emp" + is_optional: false + parent_frd_id: "^14661" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.payment_option" + is_optional: false + parent_frd_id: "^1882" + } + inputs { + type: PARENT_FRD + consult_frd_id: "Form.phone_number" + is_optional: false + parent_frd_id: "^30248" + } +} +################################################################# +# csv_to_proto: Test failure. +################################################################# +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} +consult_settings { + taxonomy_id: 9249441 + view_id: "Routing - Ads_certs" + max_wait_time_for_consult_secs: 86400 + reroute_on_customer_interaction: true + 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: "ORGANIZATION" + value: "trust_safety" + } + 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: "^68613" + } +} diff --git a/universe/ac_types/parse.py b/universe/ac_types/parse.py new file mode 100644 index 000000000000..872f3f0c3fcc --- /dev/null +++ b/universe/ac_types/parse.py @@ -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 diff --git a/universe/ac_types/parse.pyc b/universe/ac_types/parse.pyc new file mode 100644 index 000000000000..b1be813ba770 --- /dev/null +++ b/universe/ac_types/parse.pyc Binary files differdiff --git a/universe/ac_types/prelude.py b/universe/ac_types/prelude.py new file mode 100644 index 000000000000..21a809288fd8 --- /dev/null +++ b/universe/ac_types/prelude.py @@ -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") diff --git a/universe/ac_types/pretty.py b/universe/ac_types/pretty.py new file mode 100644 index 000000000000..ac1f7203187b --- /dev/null +++ b/universe/ac_types/pretty.py @@ -0,0 +1,7 @@ +from pprint import PrettyPrinter + +printer = PrettyPrinter(indent=2) + + +def pretty_print(x): + return printer.pprint(x) diff --git a/universe/ac_types/pretty.pyc b/universe/ac_types/pretty.pyc new file mode 100644 index 000000000000..1f735fdb25e0 --- /dev/null +++ b/universe/ac_types/pretty.pyc Binary files differdiff --git a/universe/ac_types/regex.py b/universe/ac_types/regex.py new file mode 100644 index 000000000000..e5203bedfae4 --- /dev/null +++ b/universe/ac_types/regex.py @@ -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") diff --git a/universe/ac_types/scrape.py b/universe/ac_types/scrape.py new file mode 100644 index 000000000000..a68bdcc9afca --- /dev/null +++ b/universe/ac_types/scrape.py @@ -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) diff --git a/universe/ac_types/serialize.py b/universe/ac_types/serialize.py new file mode 100644 index 000000000000..966a9024f3fa --- /dev/null +++ b/universe/ac_types/serialize.py @@ -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') diff --git a/universe/ac_types/string.py b/universe/ac_types/string.py new file mode 100644 index 000000000000..9ef6c3ab52a6 --- /dev/null +++ b/universe/ac_types/string.py @@ -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") diff --git a/universe/ac_types/string.pyc b/universe/ac_types/string.pyc new file mode 100644 index 000000000000..df93a05fd5cd --- /dev/null +++ b/universe/ac_types/string.pyc Binary files differdiff --git a/universe/ac_types/test_utils.py b/universe/ac_types/test_utils.py new file mode 100644 index 000000000000..1c1570754eb6 --- /dev/null +++ b/universe/ac_types/test_utils.py @@ -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) diff --git a/universe/ac_types/test_utils.pyc b/universe/ac_types/test_utils.pyc new file mode 100644 index 000000000000..ed6d0ea9642f --- /dev/null +++ b/universe/ac_types/test_utils.pyc Binary files differdiff --git a/universe/ac_types/todo.org b/universe/ac_types/todo.org new file mode 100644 index 000000000000..e4e274cc3d21 --- /dev/null +++ b/universe/ac_types/todo.org @@ -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. diff --git a/universe/advent-of-code/aoc2019.nix b/universe/advent-of-code/aoc2019.nix new file mode 100644 index 000000000000..10e6c559a9b4 --- /dev/null +++ b/universe/advent-of-code/aoc2019.nix @@ -0,0 +1,8 @@ +with import <nixpkgs> {}; +with python35Packages; + +buildPythonPackage { + name = "wpcarro"; + src = ./day_5.py; + propagatedBuildInputs = [ pytest numpy ]; +} diff --git a/universe/advent-of-code/day_1.py b/universe/advent-of-code/day_1.py new file mode 100644 index 000000000000..bd4024e3ec7d --- /dev/null +++ b/universe/advent-of-code/day_1.py @@ -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)) diff --git a/universe/advent-of-code/day_2.py b/universe/advent-of-code/day_2.py new file mode 100644 index 000000000000..77774c1bb5ad --- /dev/null +++ b/universe/advent-of-code/day_2.py @@ -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) diff --git a/universe/advent-of-code/day_3.py b/universe/advent-of-code/day_3.py new file mode 100644 index 000000000000..6dd863528c1c --- /dev/null +++ b/universe/advent-of-code/day_3.py @@ -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)) diff --git a/universe/advent-of-code/day_4.py b/universe/advent-of-code/day_4.py new file mode 100644 index 000000000000..adef73b452dc --- /dev/null +++ b/universe/advent-of-code/day_4.py @@ -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)])) diff --git a/universe/advent-of-code/day_5.ex b/universe/advent-of-code/day_5.ex new file mode 100644 index 000000000000..807e3c9177be --- /dev/null +++ b/universe/advent-of-code/day_5.ex @@ -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 diff --git a/universe/advent-of-code/day_5.py b/universe/advent-of-code/day_5.py new file mode 100644 index 000000000000..3d82846e6126 --- /dev/null +++ b/universe/advent-of-code/day_5.py @@ -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)) diff --git a/universe/advent-of-code/day_5.pyc b/universe/advent-of-code/day_5.pyc new file mode 100644 index 000000000000..d95573e94326 --- /dev/null +++ b/universe/advent-of-code/day_5.pyc Binary files differdiff --git a/universe/advent-of-code/day_6.py b/universe/advent-of-code/day_6.py new file mode 100644 index 000000000000..aba99b8239ff --- /dev/null +++ b/universe/advent-of-code/day_6.py @@ -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)) diff --git a/universe/advent-of-code/day_7.py b/universe/advent-of-code/day_7.py new file mode 100644 index 000000000000..14597d5104e3 --- /dev/null +++ b/universe/advent-of-code/day_7.py @@ -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) diff --git a/universe/advent-of-code/writePythonBin.nix b/universe/advent-of-code/writePythonBin.nix new file mode 100644 index 000000000000..1730edb2ebe4 --- /dev/null +++ b/universe/advent-of-code/writePythonBin.nix @@ -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} + ''; +} diff --git a/universe/blog/default.nix b/universe/blog/default.nix new file mode 100644 index 000000000000..5359a0ab6f37 --- /dev/null +++ b/universe/blog/default.nix @@ -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 + ]; +} diff --git a/universe/blog/posts/test.md b/universe/blog/posts/test.md new file mode 100644 index 000000000000..ec2e030b2824 --- /dev/null +++ b/universe/blog/posts/test.md @@ -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. diff --git a/universe/blog/src/index.html b/universe/blog/src/index.html new file mode 100644 index 000000000000..a9d9cf7eaa25 --- /dev/null +++ b/universe/blog/src/index.html @@ -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> diff --git a/universe/blog/src/server.lisp b/universe/blog/src/server.lisp new file mode 100644 index 000000000000..ad8169fa1af9 --- /dev/null +++ b/universe/blog/src/server.lisp @@ -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)))) diff --git a/universe/clojure/buildClojure.nix b/universe/clojure/buildClojure.nix new file mode 100644 index 000000000000..1596279de1c5 --- /dev/null +++ b/universe/clojure/buildClojure.nix @@ -0,0 +1,11 @@ +{ universe ? import <universe> {}, ... }: + +universe.nix.buildClojure.program { + name = "test"; + deps = with universe.third_party.clojure; [ + + ]; + srcs = [ + ./main.clj + ] +} diff --git a/universe/crack_the_coding_interview/11_1.py b/universe/crack_the_coding_interview/11_1.py new file mode 100644 index 000000000000..ec7b65dae0c3 --- /dev/null +++ b/universe/crack_the_coding_interview/11_1.py @@ -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!') diff --git a/universe/crack_the_coding_interview/to_tree.hs b/universe/crack_the_coding_interview/to_tree.hs new file mode 100644 index 000000000000..8496d88c0c0c --- /dev/null +++ b/universe/crack_the_coding_interview/to_tree.hs @@ -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) diff --git a/universe/data_structures_and_algorithms/array-traversals.py b/universe/data_structures_and_algorithms/array-traversals.py new file mode 100644 index 000000000000..35cb4392812e --- /dev/null +++ b/universe/data_structures_and_algorithms/array-traversals.py @@ -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) diff --git a/universe/data_structures_and_algorithms/balanced-binary-tree.py b/universe/data_structures_and_algorithms/balanced-binary-tree.py new file mode 100644 index 000000000000..01fd965fd540 --- /dev/null +++ b/universe/data_structures_and_algorithms/balanced-binary-tree.py @@ -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) diff --git a/universe/data_structures_and_algorithms/bit-manipulation.py b/universe/data_structures_and_algorithms/bit-manipulation.py new file mode 100644 index 000000000000..dc30bb508887 --- /dev/null +++ b/universe/data_structures_and_algorithms/bit-manipulation.py @@ -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)) diff --git a/universe/data_structures_and_algorithms/bracket-validator.py b/universe/data_structures_and_algorithms/bracket-validator.py new file mode 100644 index 000000000000..a50f8b074e55 --- /dev/null +++ b/universe/data_structures_and_algorithms/bracket-validator.py @@ -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) diff --git a/universe/data_structures_and_algorithms/bst-checker.py b/universe/data_structures_and_algorithms/bst-checker.py new file mode 100644 index 000000000000..689be97a8503 --- /dev/null +++ b/universe/data_structures_and_algorithms/bst-checker.py @@ -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) diff --git a/universe/data_structures_and_algorithms/cafe-order-checker.py b/universe/data_structures_and_algorithms/cafe-order-checker.py new file mode 100644 index 000000000000..e34a2b136ab6 --- /dev/null +++ b/universe/data_structures_and_algorithms/cafe-order-checker.py @@ -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) diff --git a/universe/data_structures_and_algorithms/cake-thief.py b/universe/data_structures_and_algorithms/cake-thief.py new file mode 100644 index 000000000000..9eddb34b2db3 --- /dev/null +++ b/universe/data_structures_and_algorithms/cake-thief.py @@ -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) diff --git a/universe/data_structures_and_algorithms/coins.py b/universe/data_structures_and_algorithms/coins.py new file mode 100644 index 000000000000..eb5754f98210 --- /dev/null +++ b/universe/data_structures_and_algorithms/coins.py @@ -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) diff --git a/universe/data_structures_and_algorithms/conways-game-of-life.py b/universe/data_structures_and_algorithms/conways-game-of-life.py new file mode 100644 index 000000000000..3836bcd0c653 --- /dev/null +++ b/universe/data_structures_and_algorithms/conways-game-of-life.py @@ -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) diff --git a/universe/data_structures_and_algorithms/delete-node.py b/universe/data_structures_and_algorithms/delete-node.py new file mode 100644 index 000000000000..7e431e224962 --- /dev/null +++ b/universe/data_structures_and_algorithms/delete-node.py @@ -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) diff --git a/universe/data_structures_and_algorithms/dft.py b/universe/data_structures_and_algorithms/dft.py new file mode 100644 index 000000000000..127d48c1864b --- /dev/null +++ b/universe/data_structures_and_algorithms/dft.py @@ -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() diff --git a/universe/data_structures_and_algorithms/dijkstra-shortest-path.py b/universe/data_structures_and_algorithms/dijkstra-shortest-path.py new file mode 100644 index 000000000000..03907f604044 --- /dev/null +++ b/universe/data_structures_and_algorithms/dijkstra-shortest-path.py @@ -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)) diff --git a/universe/data_structures_and_algorithms/find-duplicate-optimize-for-space-beast.py b/universe/data_structures_and_algorithms/find-duplicate-optimize-for-space-beast.py new file mode 100644 index 000000000000..93fdd9eed2d6 --- /dev/null +++ b/universe/data_structures_and_algorithms/find-duplicate-optimize-for-space-beast.py @@ -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) diff --git a/universe/data_structures_and_algorithms/find-duplicate-optimize-for-space.py b/universe/data_structures_and_algorithms/find-duplicate-optimize-for-space.py new file mode 100644 index 000000000000..e2739f0f6055 --- /dev/null +++ b/universe/data_structures_and_algorithms/find-duplicate-optimize-for-space.py @@ -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) diff --git a/universe/data_structures_and_algorithms/find-rotation-point.py b/universe/data_structures_and_algorithms/find-rotation-point.py new file mode 100644 index 000000000000..2103a5b84f75 --- /dev/null +++ b/universe/data_structures_and_algorithms/find-rotation-point.py @@ -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) diff --git a/universe/data_structures_and_algorithms/find-unique-int-among-duplicates.py b/universe/data_structures_and_algorithms/find-unique-int-among-duplicates.py new file mode 100644 index 000000000000..dfa5de42cc0b --- /dev/null +++ b/universe/data_structures_and_algorithms/find-unique-int-among-duplicates.py @@ -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) diff --git a/universe/data_structures_and_algorithms/fixtures.py b/universe/data_structures_and_algorithms/fixtures.py new file mode 100644 index 000000000000..27689ca76d04 --- /dev/null +++ b/universe/data_structures_and_algorithms/fixtures.py @@ -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. diff --git a/universe/data_structures_and_algorithms/graph-coloring.py b/universe/data_structures_and_algorithms/graph-coloring.py new file mode 100644 index 000000000000..bc7f7ceea562 --- /dev/null +++ b/universe/data_structures_and_algorithms/graph-coloring.py @@ -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) diff --git a/universe/data_structures_and_algorithms/graph-to-graphviz.py b/universe/data_structures_and_algorithms/graph-to-graphviz.py new file mode 100644 index 000000000000..0e7e97a20ca7 --- /dev/null +++ b/universe/data_structures_and_algorithms/graph-to-graphviz.py @@ -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!') diff --git a/universe/data_structures_and_algorithms/highest-product-of-3.py b/universe/data_structures_and_algorithms/highest-product-of-3.py new file mode 100644 index 000000000000..889663e058da --- /dev/null +++ b/universe/data_structures_and_algorithms/highest-product-of-3.py @@ -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) diff --git a/universe/data_structures_and_algorithms/inflight-entertainment.py b/universe/data_structures_and_algorithms/inflight-entertainment.py new file mode 100644 index 000000000000..6e17baef3709 --- /dev/null +++ b/universe/data_structures_and_algorithms/inflight-entertainment.py @@ -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") diff --git a/universe/data_structures_and_algorithms/knapsack-0-1.py b/universe/data_structures_and_algorithms/knapsack-0-1.py new file mode 100644 index 000000000000..c72d19d4ed73 --- /dev/null +++ b/universe/data_structures_and_algorithms/knapsack-0-1.py @@ -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) diff --git a/universe/data_structures_and_algorithms/kth-to-last.py b/universe/data_structures_and_algorithms/kth-to-last.py new file mode 100644 index 000000000000..8291e54533d5 --- /dev/null +++ b/universe/data_structures_and_algorithms/kth-to-last.py @@ -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) diff --git a/universe/data_structures_and_algorithms/largest-stack.py b/universe/data_structures_and_algorithms/largest-stack.py new file mode 100644 index 000000000000..aab9671eb6d3 --- /dev/null +++ b/universe/data_structures_and_algorithms/largest-stack.py @@ -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) diff --git a/universe/data_structures_and_algorithms/linked-list-cycles.py b/universe/data_structures_and_algorithms/linked-list-cycles.py new file mode 100644 index 000000000000..75a4b993944c --- /dev/null +++ b/universe/data_structures_and_algorithms/linked-list-cycles.py @@ -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) diff --git a/universe/data_structures_and_algorithms/merge-sort.py b/universe/data_structures_and_algorithms/merge-sort.py new file mode 100644 index 000000000000..6dbe0fa0f3c3 --- /dev/null +++ b/universe/data_structures_and_algorithms/merge-sort.py @@ -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])) diff --git a/universe/data_structures_and_algorithms/merging-ranges.py b/universe/data_structures_and_algorithms/merging-ranges.py new file mode 100644 index 000000000000..4e3604d5bcca --- /dev/null +++ b/universe/data_structures_and_algorithms/merging-ranges.py @@ -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) diff --git a/universe/data_structures_and_algorithms/mesh-message.gv b/universe/data_structures_and_algorithms/mesh-message.gv new file mode 100644 index 000000000000..1e67c3954f5f --- /dev/null +++ b/universe/data_structures_and_algorithms/mesh-message.gv @@ -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} +} diff --git a/universe/data_structures_and_algorithms/mesh-message.py b/universe/data_structures_and_algorithms/mesh-message.py new file mode 100644 index 000000000000..c9d7d9d74151 --- /dev/null +++ b/universe/data_structures_and_algorithms/mesh-message.py @@ -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) diff --git a/universe/data_structures_and_algorithms/norman.py b/universe/data_structures_and_algorithms/norman.py new file mode 100644 index 000000000000..379ba92abba8 --- /dev/null +++ b/universe/data_structures_and_algorithms/norman.py @@ -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 diff --git a/universe/data_structures_and_algorithms/nth-fibonacci.py b/universe/data_structures_and_algorithms/nth-fibonacci.py new file mode 100644 index 000000000000..cdb2846ea338 --- /dev/null +++ b/universe/data_structures_and_algorithms/nth-fibonacci.py @@ -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) diff --git a/universe/data_structures_and_algorithms/optimal-stopping.py b/universe/data_structures_and_algorithms/optimal-stopping.py new file mode 100644 index 000000000000..af13239941d0 --- /dev/null +++ b/universe/data_structures_and_algorithms/optimal-stopping.py @@ -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)) diff --git a/universe/data_structures_and_algorithms/perm-tree.py b/universe/data_structures_and_algorithms/perm-tree.py new file mode 100644 index 000000000000..0eb389c26bb9 --- /dev/null +++ b/universe/data_structures_and_algorithms/perm-tree.py @@ -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) diff --git a/universe/data_structures_and_algorithms/permutation-palindrome.py b/universe/data_structures_and_algorithms/permutation-palindrome.py new file mode 100644 index 000000000000..0a2136a408f2 --- /dev/null +++ b/universe/data_structures_and_algorithms/permutation-palindrome.py @@ -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) diff --git a/universe/data_structures_and_algorithms/permutations.py b/universe/data_structures_and_algorithms/permutations.py new file mode 100644 index 000000000000..fc2c1ef7eebc --- /dev/null +++ b/universe/data_structures_and_algorithms/permutations.py @@ -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)) diff --git a/universe/data_structures_and_algorithms/plot.py b/universe/data_structures_and_algorithms/plot.py new file mode 100644 index 000000000000..5601891a0d9b --- /dev/null +++ b/universe/data_structures_and_algorithms/plot.py @@ -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() diff --git a/universe/data_structures_and_algorithms/product-of-other-numbers.py b/universe/data_structures_and_algorithms/product-of-other-numbers.py new file mode 100644 index 000000000000..d05e82d42b02 --- /dev/null +++ b/universe/data_structures_and_algorithms/product-of-other-numbers.py @@ -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) diff --git a/universe/data_structures_and_algorithms/queue-two-stacks.py b/universe/data_structures_and_algorithms/queue-two-stacks.py new file mode 100644 index 000000000000..63da08ebf79a --- /dev/null +++ b/universe/data_structures_and_algorithms/queue-two-stacks.py @@ -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) diff --git a/universe/data_structures_and_algorithms/rectangular-love.py b/universe/data_structures_and_algorithms/rectangular-love.py new file mode 100644 index 000000000000..47c0f53979c6 --- /dev/null +++ b/universe/data_structures_and_algorithms/rectangular-love.py @@ -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) diff --git a/universe/data_structures_and_algorithms/recursive-string-permutations.py b/universe/data_structures_and_algorithms/recursive-string-permutations.py new file mode 100644 index 000000000000..70461ddf5dac --- /dev/null +++ b/universe/data_structures_and_algorithms/recursive-string-permutations.py @@ -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) diff --git a/universe/data_structures_and_algorithms/reverse-linked-list.py b/universe/data_structures_and_algorithms/reverse-linked-list.py new file mode 100644 index 000000000000..b7396b20ce3f --- /dev/null +++ b/universe/data_structures_and_algorithms/reverse-linked-list.py @@ -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) diff --git a/universe/data_structures_and_algorithms/reverse-words.py b/universe/data_structures_and_algorithms/reverse-words.py new file mode 100644 index 000000000000..5df12ebabdc7 --- /dev/null +++ b/universe/data_structures_and_algorithms/reverse-words.py @@ -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) diff --git a/universe/data_structures_and_algorithms/second-largest-item-bst.py b/universe/data_structures_and_algorithms/second-largest-item-bst.py new file mode 100644 index 000000000000..bc167d975a7b --- /dev/null +++ b/universe/data_structures_and_algorithms/second-largest-item-bst.py @@ -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) diff --git a/universe/data_structures_and_algorithms/shortest-path-inject-vertices.py b/universe/data_structures_and_algorithms/shortest-path-inject-vertices.py new file mode 100644 index 000000000000..e08ea66b8f50 --- /dev/null +++ b/universe/data_structures_and_algorithms/shortest-path-inject-vertices.py @@ -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)) diff --git a/universe/data_structures_and_algorithms/shuffle.py b/universe/data_structures_and_algorithms/shuffle.py new file mode 100644 index 000000000000..bdfbad24263c --- /dev/null +++ b/universe/data_structures_and_algorithms/shuffle.py @@ -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[:])) diff --git a/universe/data_structures_and_algorithms/string-reverse.py b/universe/data_structures_and_algorithms/string-reverse.py new file mode 100644 index 000000000000..8b4cdac1c271 --- /dev/null +++ b/universe/data_structures_and_algorithms/string-reverse.py @@ -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") diff --git a/universe/data_structures_and_algorithms/temperature-tracker.py b/universe/data_structures_and_algorithms/temperature-tracker.py new file mode 100644 index 000000000000..6b042182f01c --- /dev/null +++ b/universe/data_structures_and_algorithms/temperature-tracker.py @@ -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) diff --git a/universe/data_structures_and_algorithms/test.txt b/universe/data_structures_and_algorithms/test.txt new file mode 100644 index 000000000000..ce013625030b --- /dev/null +++ b/universe/data_structures_and_algorithms/test.txt @@ -0,0 +1 @@ +hello diff --git a/universe/data_structures_and_algorithms/top-scores.py b/universe/data_structures_and_algorithms/top-scores.py new file mode 100644 index 000000000000..8e7b073dd8bd --- /dev/null +++ b/universe/data_structures_and_algorithms/top-scores.py @@ -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!") diff --git a/universe/data_structures_and_algorithms/topo-sort.py b/universe/data_structures_and_algorithms/topo-sort.py new file mode 100644 index 000000000000..fe295b0279ff --- /dev/null +++ b/universe/data_structures_and_algorithms/topo-sort.py @@ -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)) diff --git a/universe/data_structures_and_algorithms/trickling-water.py b/universe/data_structures_and_algorithms/trickling-water.py new file mode 100644 index 000000000000..45621990ecf9 --- /dev/null +++ b/universe/data_structures_and_algorithms/trickling-water.py @@ -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!") diff --git a/universe/data_structures_and_algorithms/which-appears-twice.py b/universe/data_structures_and_algorithms/which-appears-twice.py new file mode 100644 index 000000000000..e9a4f0eb24d0 --- /dev/null +++ b/universe/data_structures_and_algorithms/which-appears-twice.py @@ -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) diff --git a/universe/deepmind/balanced-binary-tree.py b/universe/deepmind/balanced-binary-tree.py new file mode 100644 index 000000000000..7fc174a2a9f3 --- /dev/null +++ b/universe/deepmind/balanced-binary-tree.py @@ -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) diff --git a/universe/deepmind/dijkstra.py b/universe/deepmind/dijkstra.py new file mode 100644 index 000000000000..6975dbe4d1d6 --- /dev/null +++ b/universe/deepmind/dijkstra.py @@ -0,0 +1,26 @@ +# Doing a practice implementation of Dijkstra's algorithm: a priority-first +# search. +from heapq import heappush, heappop + + +class Node(object): + def __init__(self, value, children): + self.value = value + self.children = children + + +def shortest_path(a, b): + """Return the shortest path from `a` to `b`.""" + q = [] + seen = set() + heappush((a.value, a, [a]), q) + + while q: + d, node, path = heappop(q) + if node == b: + return path + seen.add(node) + for child in node.children: + if child not in seen: + heappush((d + child.value, child, path + [child]), q) + raise Exception("Path between nodes A and B does not exist.") diff --git a/universe/deepmind/efficiency.org b/universe/deepmind/efficiency.org new file mode 100644 index 000000000000..89a45c52ad8a --- /dev/null +++ b/universe/deepmind/efficiency.org @@ -0,0 +1,6 @@ +* Sorting +** Merge: O(n*log(n)) +** Heap: O(n*log(n)) +** Insertion: O(n^2) +** Quick: O(n^2) +** Bubble: O(n^2) diff --git a/universe/deepmind/find-rotation-point.py b/universe/deepmind/find-rotation-point.py new file mode 100644 index 000000000000..5c21d5167ce9 --- /dev/null +++ b/universe/deepmind/find-rotation-point.py @@ -0,0 +1,55 @@ +import unittest +from math import floor + + +def midpoint(a, b): + return a + floor((b - a) / 2) + + +def do_find_rotation_point(a, b, xs): + i = midpoint(a, b) + count = b - a + 1 + + if count == 2: + if xs[a] > xs[b]: + return b + else: + return -1 + + if i in {a, b}: + return i + + if xs[a] < xs[i]: + return do_find_rotation_point(i, b, xs) + else: + return do_find_rotation_point(a, i, xs) + + +def find_rotation_point(xs): + return do_find_rotation_point(0, len(xs) - 1, xs) + + +# 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) + + +unittest.main(verbosity=2) diff --git a/universe/deepmind/inflight-entertainment.py b/universe/deepmind/inflight-entertainment.py new file mode 100644 index 000000000000..2116b27b0b97 --- /dev/null +++ b/universe/deepmind/inflight-entertainment.py @@ -0,0 +1,51 @@ +import unittest + + +def can_two_movies_fill_flight(xs, t): + seeking = set() + for x in xs: + if x in seeking: + return True + else: + seeking.add(t - x) + return False + + +# Tests + + +class Test(unittest.TestCase): + def test_short_flight(self): + result = can_two_movies_fill_flight([2, 4], 1) + self.assertFalse(result) + + def test_long_flight(self): + result = can_two_movies_fill_flight([2, 4], 6) + self.assertTrue(result) + + def test_one_movie_half_flight_length(self): + result = can_two_movies_fill_flight([3, 8], 6) + self.assertFalse(result) + + def test_two_movies_half_flight_length(self): + result = can_two_movies_fill_flight([3, 8, 3], 6) + self.assertTrue(result) + + def test_lots_of_possible_pairs(self): + result = can_two_movies_fill_flight([1, 2, 3, 4, 5, 6], 7) + self.assertTrue(result) + + def test_not_using_first_movie(self): + result = can_two_movies_fill_flight([4, 3, 2], 5) + self.assertTrue(result) + + def test_only_one_movie(self): + result = can_two_movies_fill_flight([6], 6) + self.assertFalse(result) + + def test_no_movies(self): + result = can_two_movies_fill_flight([], 2) + self.assertFalse(result) + + +unittest.main(verbosity=2) diff --git a/universe/deepmind/kth-to-last.py b/universe/deepmind/kth-to-last.py new file mode 100644 index 000000000000..5335e419f7ec --- /dev/null +++ b/universe/deepmind/kth-to-last.py @@ -0,0 +1,64 @@ +import unittest + + +def kth_to_last_node(k, x): + a, b = x, x + + if k == 0: + raise Exception('Value of 0 for k is not supported') + + for _ in range(k - 1): + if not a.next: + raise Exception('Value of {} for k is too large'.format(k)) + a = a.next + + while a.next: + a, b = a.next, b.next + return b + + +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) diff --git a/universe/deepmind/merging-ranges.py b/universe/deepmind/merging-ranges.py new file mode 100644 index 000000000000..23b40793b8f1 --- /dev/null +++ b/universe/deepmind/merging-ranges.py @@ -0,0 +1,59 @@ +import unittest + + +def merge_ranges(xs): + xs.sort() + result = [xs[0]] + for curr in xs[1:]: + a, z = result[-1] + if z >= curr[0]: + result[-1] = (a, max(z, curr[1])) + else: + result.append(curr) + 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) diff --git a/universe/deepmind/recursive-string-permutations.py b/universe/deepmind/recursive-string-permutations.py new file mode 100644 index 000000000000..f50db2838707 --- /dev/null +++ b/universe/deepmind/recursive-string-permutations.py @@ -0,0 +1,56 @@ +import unittest +from itertools import permutations + + +class Node(object): + def __init__(self, x): + self.value = x + self.children = [] + + +def make_tree(c, xs): + root = Node(c) + for x in xs: + root.children.append(make_tree(x, xs - {x})) + return root + + +def get_permutations(xs): + xs = set(xs) + root = make_tree("", xs) + q, perms = [], set() + q.append(("", root)) + while q: + c, node = q.pop() + if not node.children: + perms.add(c) + else: + for child in node.children: + q.append((c + child.value, child)) + return perms + + +# 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) diff --git a/universe/deepmind/reverse-linked-list.py b/universe/deepmind/reverse-linked-list.py new file mode 100644 index 000000000000..82fac171d5d1 --- /dev/null +++ b/universe/deepmind/reverse-linked-list.py @@ -0,0 +1,74 @@ +import unittest + + +def reverse(node): + prev = None + next = None + curr = node + + while curr: + next = curr.next + curr.next = prev + prev = curr + curr = next + + 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) diff --git a/universe/deepmind/stock-price.py b/universe/deepmind/stock-price.py new file mode 100644 index 000000000000..7055b66af196 --- /dev/null +++ b/universe/deepmind/stock-price.py @@ -0,0 +1,51 @@ +def get_max_profit(xs): + best_profit = xs[1] - xs[0] + lowest_buy = xs[0] + + for x in xs[1:]: + best_profit = max(best_profit, x - lowest_buy) + lowest_buy = min(lowest_buy, x) + return best_profit + + +# Tests + +import unittest + + +class Test(unittest.TestCase): + def test_price_goes_up_then_down(self): + actual = get_max_profit([1, 5, 3, 2]) + expected = 4 + self.assertEqual(actual, expected) + + def test_price_goes_down_then_up(self): + actual = get_max_profit([7, 2, 8, 9]) + expected = 7 + self.assertEqual(actual, expected) + + def test_price_goes_up_all_day(self): + actual = get_max_profit([1, 6, 7, 9]) + expected = 8 + self.assertEqual(actual, expected) + + def test_price_goes_down_all_day(self): + actual = get_max_profit([9, 7, 4, 1]) + expected = -2 + self.assertEqual(actual, expected) + + def test_price_stays_the_same_all_day(self): + actual = get_max_profit([1, 1, 1, 1]) + expected = 0 + self.assertEqual(actual, expected) + + def test_error_with_empty_prices(self): + with self.assertRaises(Exception): + get_max_profit([]) + + def test_error_with_one_price(self): + with self.assertRaises(Exception): + get_max_profit([1]) + + +unittest.main(verbosity=2) diff --git a/universe/deepmind/which-appears-twice.py b/universe/deepmind/which-appears-twice.py new file mode 100644 index 000000000000..c01379295d32 --- /dev/null +++ b/universe/deepmind/which-appears-twice.py @@ -0,0 +1,29 @@ +import unittest + + +def find_repeat(xs): + n = max(xs) + expected_sum = (n + 1) * n / 2 + actual_sum = sum(xs) + return actual_sum - expected_sum + + +# 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) diff --git a/universe/default.nix b/universe/default.nix new file mode 100644 index 000000000000..ff4f40d8f943 --- /dev/null +++ b/universe/default.nix @@ -0,0 +1,37 @@ +# At the time of this writing, this configuration was taken from @tazjin's +# default.nix from his depot. I've added, changed, and removed that parts that I +# don't need, and this is what remains. +{ ... }@args: + +with builtins; + +let + fix = f: let x = f x; in x; + + # Global configuration that all packages are called with. + config = pkgs: { + inherit pkgs; + }; + + readTree' = import ~/depot/nix/readTree {}; + + # TODO: Find a better way to expose entire monorepo without introducing + # "infinite recursion". + localPkgs = readTree: { + blog = readTree ./blog; + lisp = readTree ./lisp; + third_party = readTree ./third_party; + }; +in fix(self: { + config = config self; + + # Expose readTree for downstream repo consumers. + readTree = { + __functor = x: (readTree' x.config); + config = self.config; + }; +} + +# Add local packages as structured by readTree +// (localPkgs (readTree' (self.config // { inherit (self) lib; }))) +) diff --git a/universe/docker/README.md b/universe/docker/README.md new file mode 100644 index 000000000000..34f762cc1970 --- /dev/null +++ b/universe/docker/README.md @@ -0,0 +1,59 @@ +# Deployments + +I'm documenting how I currently deploy things. + +I'd like to automate this workflow as much as possible, and I intend to do just +that. For now, I'm running things manually until I can design an generalization +that appeals to me. + +## Dependencies +- `nix-build` +- `docker` +- `gcloud` + +## Step-by-step + +1. Use `nix-build` to create our Docker image for Cloud Run. + +```shell +> nix-build ./cloud_run.nix +``` + +This outputs a Docker image at `./result`. + +1. Load the built image (i.e. `./result`) into `docker` so that we can tag it + and push it to the Google Container Registry (i.e. GCR). + +```shell +> sudo docker load <./result +``` + +1. (Optionally) Run the image locally to verify its integrity. + +```shell +> sudo docker run -d <name>:<tag> +``` + +1. Tag and push the image to GCR. + +```shell +> sudo docker tag <name>:<label> gcr.io/<google-cloud-project-id>/<name>:<latest> +``` + +# TODO: Prefer using a command line tool like `gcloud` for these steps. + +1. Visit Google Cloud Run; create a new service with "Create Service"; select + the uploaded Docker image from the "Container Image URL" field; click + "Create" to deploy. + + +## Notes + +You may need to authorize `gcloud` by running the following: + +```shell +> sudo gcloud auth login --no-launch-browser +``` + +You must use `sudo` here since the `docker` invocations are prefixed with `sudo` +as well. diff --git a/universe/docker/cloud_run.nix b/universe/docker/cloud_run.nix new file mode 100644 index 000000000000..70be4040c36b --- /dev/null +++ b/universe/docker/cloud_run.nix @@ -0,0 +1,18 @@ +{ + pkgs ? import <nixpkgs> {}, + depot ? import <depot> {}, + ... +}: + +pkgs.dockerTools.buildLayeredImage { + name = "gemma"; + tag = "latest"; + config.ExposedPorts = { + "4242" = {}; + }; + config.Env = [ + "GEMMA_CONFIG=${./config.lisp}" + ]; + config.Cmd = [ "${depot.fun.gemma}/bin/gemma" ]; + maxLayers = 120; +} diff --git a/universe/docker/config.lisp b/universe/docker/config.lisp new file mode 100644 index 000000000000..54f8e5f34462 --- /dev/null +++ b/universe/docker/config.lisp @@ -0,0 +1,21 @@ +;; Example configuration file for Gemma + +(config :port 4242 + :data-dir "/tmp/gemma/") + +(deftask bathroom/wipe-mirror 7) +(deftask bathroom/wipe-counter 7) + +;; Bedroom tasks +(deftask bedroom/change-sheets 7) +(deftask bedroom/vacuum 10) + +;; Kitchen tasks +(deftask kitchen/normal-trash 3) +(deftask kitchen/green-trash 5) +(deftask kitchen/blue-trash 5) +(deftask kitchen/wipe-counters 3) +(deftask kitchen/vacuum 5 "Kitchen has more crumbs and such!") + +;; Entire place +(deftask clean-windows 60) diff --git a/universe/haskell-file/f-todo.org b/universe/haskell-file/f-todo.org new file mode 100644 index 000000000000..6dd43a96291f --- /dev/null +++ b/universe/haskell-file/f-todo.org @@ -0,0 +1,67 @@ +* Paths +** TODO f-join (&rest args) +** TODO f-split (path) +** TODO f-expand (path &optional dir) +** TODO f-filename (path) +** TODO f-dirname (path) +** TODO f-common-parent (paths) +** TODO f-ext (path) +** TODO f-no-ext (path) +** TODO f-swap-ext (path ext) +** TODO f-base (path) +** TODO f-relative (path &optional dir) +** TODO f-short (path) +** TODO f-long (path) +** TODO f-canonical (path) +** TODO f-slash (path) +** TODO f-full (path) +** TODO f-uniquify (paths) +** TODO f-uniquify-alist (paths) +* I/O +** TODO f-read-bytes (path) +** TODO f-write-bytes (data path) +** TODO f-read-text (path &optional coding) +** TODO f-write-text(text coding path) +** TODO f-append-text(text coding path) +** TODO f-append-bytes(text coding path) +** TODO Destructive +** TODO f-mkdir (&rest dirs) +** TODO f-delete (path &optional force) +** TODO f-symlink (source path) +** TODO f-move (from to) +** TODO f-copy (from to) +** TODO f-copy-contenst (from to) +** TODO f-touch (path) +** TODO Predicates +** TODO f-exists? (path) +** TODO f-directory? (path) +** TODO f-file? (path) +** TODO f-symlink? (path) +** TODO f-readable? (path) +** TODO f-writable? (path) +** TODO f-executable? (path) +** TODO f-absolute? (path) +** TODO f-relative? (path) +** TODO f-root? (path) +** TODO f-ext? (path ext) +** TODO f-same? (path-a path-b) +** TODO f-parent-of? (path-a path-b) +** TODO f-child-of? (path-a path-b) +** TODO f-ancestor-of? (path-a path-b) +** TODO f-descendant-of? (path-a path-b) +** TODO f-hidden? (path) +** TODO f-empty? (path) +** TODO Stats +** TODO f-size (path) +** f-depth (path) + +* Misc +** TODO f-this-file () +** TODO f-path-separator () +** TODO f-glob (pattern &optional path) +** TODO f-entries (path &optional fn recursive) +** TODO f-directories (path &optional fn recursive) +** TODO f-files (path &optional fn recursive) +** TODO f-root () +** TODO f-traverse-upwards (fn &optional path) +** TODO f-with-sandbox (path-or-paths &rest body) diff --git a/universe/haskell-file/f.hs b/universe/haskell-file/f.hs new file mode 100644 index 000000000000..295575f3f48d --- /dev/null +++ b/universe/haskell-file/f.hs @@ -0,0 +1,64 @@ +module F + ( join + , split + ) where + +-------------------------------------------------------------------------------- +-- Dependencies +-------------------------------------------------------------------------------- + +import Data.List (span) +import System.FilePath (FilePath, pathSeparator) +import System.FilePath.Posix (FilePath) +import qualified System.FilePath.Posix as F + +-- TODO: Move this to a misc.hs, prelude.hs, operators.hs; somewhere. +(|>) :: a -> (a -> b) -> b +(|>) a f = f a +infixl 1 |> + +-- TODO: Move this to a test_utils.hs or elsewhere. +simpleAssert :: (Eq a) => a -> a -> () +simpleAssert x y = + if x == y then + () + else + error "Assertion error" + +-------------------------------------------------------------------------------- +-- Library +-------------------------------------------------------------------------------- + +join :: [FilePath] -> FilePath +join = F.joinPath + +-- | Split path and return list containing parts. +split :: FilePath -> [String] +split = splitJoin . span (/= pathSeparator) + where + splitJoin :: (String, String) -> [String] + splitJoin ([], []) = [] + splitJoin (a, []) = [a] + splitJoin (a, [_]) = [a] + splitJoin (a, _:b) = a : split b + +-------------------------------------------------------------------------------- +-- Tests +-------------------------------------------------------------------------------- + +expected :: [([FilePath], FilePath)] +expected = [ (["path"], "path") + , (["/path"], "/path") + , (["path", "to", "file"], "path/to/file") + , (["/path", "to", "file"], "/path/to/file") + , (["/"], "/") + ] + +runTests :: [()] +runTests = + fmap (\(input, expected) -> simpleAssert (join input) expected) expected + +main :: IO () +main = do + print runTests + pure () diff --git a/universe/haskell-file/shell.nix b/universe/haskell-file/shell.nix new file mode 100644 index 000000000000..f2621d6eac5a --- /dev/null +++ b/universe/haskell-file/shell.nix @@ -0,0 +1,9 @@ +with import <nixpkgs> {}; + +stdenv.mkDerivation { + name = "f-hs"; + buildInputs = [ + (pkgs.haskellPackages.ghcWithPackages (pkgs: [ + ])) + ]; +} diff --git a/universe/haskell-file/tests.hs b/universe/haskell-file/tests.hs new file mode 100644 index 000000000000..e3967b77de1f --- /dev/null +++ b/universe/haskell-file/tests.hs @@ -0,0 +1,39 @@ +module FTest where +-------------------------------------------------------------------------------- +import Test.Tasty +import Test.Tasty.Hedgehog +import Hedgehog +-------------------------------------------------------------------------------- +import qualified Hedgehog as H +import qualified Hedgehog.Gen as Gen +import qualified Hedgehog.Range as Range +-------------------------------------------------------------------------------- +import Data.List (intercalate) +import System.FilePath (pathSeparator) +-------------------------------------------------------------------------------- +import F +-------------------------------------------------------------------------------- +main :: IO () +main + = defaultMain + . localOption (HedgehogTestLimit $ Just 50) + $ testGroup "f functions" + [ test_split + ] +-------------------------------------------------------------------------------- +test_split :: TestTree +test_split + = testGroup "split function" + [ testProperty "splits parts properly" splitSuccess + ] +splitSuccess :: Property +splitSuccess = property $ do + -- separator + -- <- H.forAll + -- $ Gen.element ['/', '\\'] + parts + <- H.forAll + . Gen.list (Range.linear 0 10) + $ Gen.list (Range.linear 1 10) Gen.alphaNum + let path = intercalate [pathSeparator] parts + F.split path === parts diff --git a/universe/java/Another.java b/universe/java/Another.java new file mode 100644 index 000000000000..e431e157336b --- /dev/null +++ b/universe/java/Another.java @@ -0,0 +1,5 @@ +public class Another { + public static String whatis() { + return "Testing"; + } +} diff --git a/universe/java/Main.java b/universe/java/Main.java new file mode 100644 index 000000000000..0bfa9ebc2a34 --- /dev/null +++ b/universe/java/Main.java @@ -0,0 +1,6 @@ +public class Main { + public static void main(String[] args) { + System.out.println(System.getProperty("java.class.path")); + System.out.println(Another.whatis()); + } +} diff --git a/universe/lisp/f/README.md b/universe/lisp/f/README.md new file mode 100644 index 000000000000..34e07180d492 --- /dev/null +++ b/universe/lisp/f/README.md @@ -0,0 +1,5 @@ +# f.lisp + +In this project, I'm attempting to port the Elisp library [`f.el`][1] to Common Lisp. + +[1]: https://github.com/rejeep/f.el diff --git a/universe/lisp/f/default.nix b/universe/lisp/f/default.nix new file mode 100644 index 000000000000..6911b2102634 --- /dev/null +++ b/universe/lisp/f/default.nix @@ -0,0 +1,15 @@ +{ + depot ? import <depot> {}, + universe ? import <universe> {}, + ... +}: + +depot.nix.buildLisp.library { + name = "f"; + deps = with universe.lisp; [ + prelude + ]; + srcs = [ + ./main.lisp + ]; +} diff --git a/universe/lisp/f/main.lisp b/universe/lisp/f/main.lisp new file mode 100644 index 000000000000..a51c38127815 --- /dev/null +++ b/universe/lisp/f/main.lisp @@ -0,0 +1,48 @@ +(in-package #:cl-user) +(defpackage #:main + (:documentation "Modern API for working with files and directories.") + (:use #:cl) + (:shadow #:type)) +(in-package #:main) + +;; Common Lisp distinguishes between `namestrings` and `pathnames` as two types +;; of filename representations. +;; +;; A `pathname` is a structured representation of the name of a file, which +;; consists of six parts: +;; 1. host +;; 2. device +;; 3. directory +;; 4. name +;; 5. type +;; 6. version + +;; TODO: Should I be using `string` as a type or `namestring`? + +(defmacro type (name in out) + `(declaim (ftype (function ,in ,out) ,name))) + +(type join (&rest namestring) pathname) +(defun join (&rest args) + "Join ARGS to a single path." + (apply #'merge-pathnames args)) + +(type ext (pathname) string) +(defun ext (path) + "Return the file extension of PATH." + (pathname-type path)) + +;; TODO: Define these tests elsewhere. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Tests +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; join +(string= (join "path") "path") +(string= (join "path" "to") "path/to") +(string= (join "/" "path" "to" "heaven") "/path/to/heaven") + +;; ext +(string= (ext #p"path/to/file.ext") "ext") +(string= (ext #p"path/to/directory") nil) diff --git a/universe/lisp/prelude.lisp b/universe/lisp/prelude.lisp new file mode 100644 index 000000000000..3522567ea0f7 --- /dev/null +++ b/universe/lisp/prelude.lisp @@ -0,0 +1,14 @@ +(in-package #:cl-user) +(defpackage #:prelude + (:documentation "Supporting miscellaneous utility functions and macros.") + (:use #:cl) + (:shadow #:type) + (:export #:type #:comment)) +(in-package #:prelude) + +;; TODO: Add documentation to these macros. + +(defmacro type (name in out) + `(declaim (ftype (function ,in ,out) ,name))) + +(defmacro comment (&rest _forms) nil) diff --git a/universe/lisp/prelude.nix b/universe/lisp/prelude.nix new file mode 100644 index 000000000000..9051f82394ff --- /dev/null +++ b/universe/lisp/prelude.nix @@ -0,0 +1,8 @@ +{ depot ? import <depot> {}, ... }: + +depot.nix.buildLisp.library { + name = "prelude"; + srcs = [ + ./prelude.lisp + ]; +} diff --git a/universe/third_party/lisp/anaphora.nix b/universe/third_party/lisp/anaphora.nix new file mode 100644 index 000000000000..04a1dd847feb --- /dev/null +++ b/universe/third_party/lisp/anaphora.nix @@ -0,0 +1,17 @@ +{ depot ? import <depot> {}, ... }: + +let + src = builtins.fetchGit { + url = "https://github.com/tokenrove/anaphora.git"; + rev = "aeace4c68cf55098a67112750b28f8f2dc6d0e30"; + }; +in depot.nix.buildLisp.library { + name = "anaphora"; + deps = []; + srcs = [ + "${src}/packages.lisp" + "${src}/early.lisp" + "${src}/symbolic.lisp" + "${src}/anaphora.lisp" + ]; +} diff --git a/universe/third_party/lisp/cl-arrows.nix b/universe/third_party/lisp/cl-arrows.nix new file mode 100644 index 000000000000..60cd8a375b1c --- /dev/null +++ b/universe/third_party/lisp/cl-arrows.nix @@ -0,0 +1,15 @@ +{ depot ? import <depot> {}, ... }: + +let + src = builtins.fetchGit { + url = "https://github.com/nightfly19/cl-arrows.git"; + rev = "cbb46b69a7de40f1161c9caaf6cef93b3af9994f"; + }; +in depot.nix.buildLisp.library { + name = "cl-arrows"; + deps = []; + srcs = [ + "${src}/packages.lisp" + "${src}/arrows.lisp" + ]; +} diff --git a/universe/third_party/lisp/cl-colors.nix b/universe/third_party/lisp/cl-colors.nix new file mode 100644 index 000000000000..2217e68ce70b --- /dev/null +++ b/universe/third_party/lisp/cl-colors.nix @@ -0,0 +1,24 @@ +{ + depot ? import <depot> {}, + universe ? import <universe> {}, + ... +}: + +let + src = builtins.fetchGit { + url = "https://github.com/tpapp/cl-colors.git"; + rev = "827410584553f5c717eec6182343b7605f707f75"; + }; +in depot.nix.buildLisp.library { + name = "cl-colors"; + deps = [ + depot.third_party.lisp.alexandria + universe.third_party.lisp.let-plus + ]; + srcs = [ + "${src}/package.lisp" + "${src}/colors.lisp" + "${src}/colornames.lisp" + "${src}/hexcolors.lisp" + ]; +} diff --git a/universe/third_party/lisp/let-plus.nix b/universe/third_party/lisp/let-plus.nix new file mode 100644 index 000000000000..a3a15776bf86 --- /dev/null +++ b/universe/third_party/lisp/let-plus.nix @@ -0,0 +1,23 @@ +{ + depot ? import <depot> {}, + universe ? import <universe> {}, + ... +}: + +let + src = builtins.fetchGit { + url = "https://github.com/tpapp/let-plus.git"; + rev = "7cf18b29ed0fe9c667a9a6a101b08ab9661a59e9"; + }; +in depot.nix.buildLisp.library { + name = "let-plus"; + deps = [ + depot.third_party.lisp.alexandria + universe.third_party.lisp.anaphora + ]; + srcs = [ + "${src}/package.lisp" + "${src}/let-plus.lisp" + "${src}/extensions.lisp" + ]; +} diff --git a/universe/third_party/lisp/linear-programming.nix b/universe/third_party/lisp/linear-programming.nix new file mode 100644 index 000000000000..432fedb8bcac --- /dev/null +++ b/universe/third_party/lisp/linear-programming.nix @@ -0,0 +1,26 @@ +{ depot ? import <depot> {}, ... }: + +let + src = builtins.fetchGit { + url = "https://github.com/neil-lindquist/linear-programming.git"; + rev = "8c8d55e7584773b90c4ba4b225c5f2008f4c474a"; + }; +in depot.nix.buildLisp.library { + name = "linear-programming"; + deps = [ + (depot.nix.buildLisp.bundled "uiop") + depot.third_party.lisp.iterate + depot.third_party.lisp.alexandria + ]; + srcs = [ + "${src}/src/conditions.lisp" + "${src}/src/expressions.lisp" + "${src}/src/simplex.lisp" + "${src}/src/system-info.lisp" + "${src}/src/utils.lisp" + "${src}/src/problem.lisp" + "${src}/src/solver.lisp" + "${src}/src/external-formats.lisp" + "${src}/src/all.lisp" + ]; +} diff --git a/universe/third_party/lisp/prove.nix b/universe/third_party/lisp/prove.nix new file mode 100644 index 000000000000..d6c0fe7413cb --- /dev/null +++ b/universe/third_party/lisp/prove.nix @@ -0,0 +1,35 @@ +{ + depot ? import <depot> {}, + universe? import <universe> {}, + ... +}: + +let + src = builtins.fetchGit { + url = "https://github.com/fukamachi/prove.git"; + rev = "5d71f02795b89e36f34e8c7d50e69b67ec6ca2de"; + }; +in depot.nix.buildLisp.library { + name = "prove"; + deps = [ + depot.third_party.lisp.cl-ppcre + depot.third_party.lisp.cl-ansi-text + depot.third_party.lisp.alexandria + depot.third_party.lisp.uiop + universe.third_party.lisp.cl-colors + ]; + srcs = [ + "${src}/src/asdf.lisp" + "${src}/src/suite.lisp" + "${src}/src/color.lisp" + "${src}/src/output.lisp" + "${src}/src/prove.lisp" + "${src}/src/report.lisp" + "${src}/src/reporter.lisp" + "${src}/src/test.lisp" + "${src}/src/reporter/dot.lisp" + "${src}/src/reporter/fiveam.lisp" + "${src}/src/reporter/list.lisp" + "${src}/src/reporter/tap.lisp" + ]; +} |