UNIT_LINE_LOOKUPS = {
    2: ("FambaaShieldGenerator", "fambaa_shield_generator"),
    4: ("RoyalCrusader", "royal_crusader"),
    6: ("Airspeeder", "airspeeder"),
    8: ("Berserker", "berserker"),
    13: ("UtilityTrawler", "utility_trawler"),
    48: ("WildFambaa", "wild_fambaa"),
    115: ("ForceMaster", "force_master"),
    118: ("Worker", "worker"),
    174: ("DestroyerDroid", "destroyer_droid"),
    180: ("ForceKnight", "force_knight"),
    307: ("Trooper", "trooper"),
    359: ("MountedTrooper", "mounted_trooper"),
    381: ("GrenadeTrooper", "grenade_trooper"),
    438: ("AntiAirTrooper", "anti_air_trooper"),
    469: ("StrikeMech", "strike_mech"),
    481: ("Ackley", "ackeley"),
    485: ("MechDestroyer", "mech_destroyer"),
    500: ("AssaultMech", "assault_mech"),
    550: ("Scout", "scout"),
    594: ("Nerf", "nerf"),
    625: ("AirCruiser", "air_cruiser"),
    641: ("JediStarfighter", "jedi_starfighter"),
    642: ("GeonosianWarrior", "geonosian_warrior"),
    691: ("Artillery", "artillery"),
    702: ("AntiAirMobile", "anti_air_mobile"),
    713: ("Pummel", "pummel"),
    762: ("Bomber", "bomber"),
    773: ("Fighter", "fighter"),
    815: ("Cruiser", "cruiser"),
    822: ("Falumpaset", "faumpaset"),
    833: ("Bantha", "bantha"),
    838: ("TransportShip", "transport_ship"),
    860: ("Nexu", "nexu"),
    868: ("Frigate", "frigate"),
    898: ("Destroyer", "destroyer"),
    918: ("AntiAirDestroyer", "anti_air_destroyer"),
    921: ("Reek", "reek"),
    931: ("CargoTrader", "cargo_trader"),
    939: ("Medic", "medic"),
    951: ("Cannon", "cannon"),
    961: ("BountyHunter", "bounty_hunter"),
    983: ("Cannon", "cannon"),
    993: ("DarkTrooper", "dark_trooper"),
    1009: ("PowerDroid", "power_droid"),
    1034: ("Probot", "probot"),
    1036: ("AirTransport", "air_transport"),
    1203: ("Mynock", "mynock"),
    1249: ("Dewback", "dewback"),
    1363: ("Kaadu", "kaadu"),
    1364: ("Ronto", "ronto"),
    1365: ("Eopie", "eopie"),
    1366: ("Tauntaun", "tauntaun"),
    1367: ("CuPa", "cu_pa"),
    1469: ("Massiff", "massiff"),
    1471: ("Orray", "orray"),
    1473: ("Shaak", "shaak"),
    1475: ("WompRat", "womp_rat"),
    1582: ("AWing", "a_wing"),
}

# key: head unit id; value: (head units of civ lines)
CIV_LINE_ASSOCS = {
    115: (89, 115, 125, 134, 136, 140, 643, 645),
    180: (52, 180, 183, 204, 232, 239, 647, 652),
    307: (238, 280, 282, 286, 307, 309, 1548, 1552),
    359: (289, 359, 361, 370, 376, 378, 1043, 1047),
    381: (292, 381, 383, 385, 387, 389, 1478, 1482),
    438: (305, 438, 440, 442, 444, 446, 1540, 1544),
    469: (244, 469, 471, 473, 475, 479, 903, 907),
    485: (246, 485, 489, 493, 495, 497, 911, 915),
    500: (249, 500, 502, 508, 514, 517, 919, 923),
    550: (258, 550, 552, 554, 556, 558, 927, 932),
    625: (619, 620, 625, 628, 630, 632, 634, 636),
    691: (203, 691, 693, 695, 698, 700, 1551, 1553),
    702: (207, 702, 704, 706, 708, 711, 1555, 1557),
    713: (231, 713, 715, 717, 719, 721, 1559, 1561),
    762: (127, 762, 764, 766, 769, 771, 1523, 1524),
    773: (158, 773, 775, 777, 779, 781, 1525, 1526),
    815: (190, 815, 818, 820, 823, 825, 1509, 1511),
    838: (191, 838, 839, 840, 841, 842, 1513, 1515),
    868: (187, 868, 870, 872, 874, 876, 1497, 1499),
    898: (186, 898, 900, 902, 904, 906, 1493, 1495),
    918: (188, 918, 920, 922, 924, 926, 1501, 1503),
    939: (167, 939, 941, 943, 945, 947, 996, 1005),
    961: (170, 961, 963, 965, 967, 969, 1533, 1535),
    951: (169, 951, 952, 953, 954, 955, 1531, 1532),
    1009: (1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014),
    1036: (1036, 1038, 1040, 1042, 1044, 1046, 1521, 1522),
}

# There is one jedi/sith for every civ
# key: jedi/sith unit id; value: switch unit id
MONK_GROUP_ASSOCS = {
    # Jedi/Sith Knight
    52: 15,
    180: 151,
    183: 152,
    204: 154,
    232: 157,
    239: 178,
    647: 648,
    652: 649,

    # Jedi/sith Master
    89: 33,
    115: 98,
    125: 100,
    134: 107,
    136: 111,
    140: 114,
    643: 1564,
    645: 1566,
}

# key: head unit id; value: (nyan object name, filename prefix)
BUILDING_LINE_LOOKUPS = {
    12: ("PowerCore", "power_core"),
    45: ("Shipyard", "shipyard"),
    49: ("HeavyWeaponsFactory", "heavy_weapons_factory"),
    50: ("Farm", "farm"),
    68: ("FoodProcCenter", "food_proc_center"),
    70: ("PrefabShelter", "prefab_shelter"),
    72: ("LightWall", "light_wall"),
    79: ("LaserTurret", "laser_turret"),
    82: ("Fortress", "fortress"),
    84: ("Spaceport", "spaceport"),
    87: ("TroopCenter", "troop_center"),
    101: ("MechFactory", "mech_factory"),
    103: ("WarCenter", "war_center"),
    104: ("Temple", "temple"),
    109: ("CommandCenter", "command_center"),
    117: ("Wall", "wall"),
    199: ("AquaHarvester", "aqua_harvester"),
    209: ("ResearchCenter", "research_center"),
    236: ("AntiAirTurret", "anti_air_turret"),
    276: ("Monument", "monument"),
    317: ("Airbase", "airbase"),
    319: ("AnimalNursery", "animal_nursery"),
    323: ("NovaProcCenter", "nova_proc_center"),
    335: ("ShieldGenerator", "shield_generator"),
    487: ("Gate", "gate"),
    562: ("CarbonProcCenter", "carbon_proc_center"),
    584: ("OreProcCenter", "ore_proc_center"),
    598: ("SentryPost", "sentry_post"),
    1576: ("SensorBuoy", "sensor_buoy"),
}

# key: (head) unit id; value: (nyan object name, filename prefix)
AMBIENT_GROUP_LOOKUPS = {
    59: ("BerryBush", "berry_bush"),
    66: ("NovaMine", "nova_mine"),
    102: ("OreMine", "ore_mine"),
    217: ("CarbonRockRed", "carbon_rock_red"),
    285: ("Holocron", "holocron"),
    348: ("OakTree", "oak_tree"),
    349: ("PalmTree", "palm_tree"),
    350: ("ScrewedTree", "screwed_tree"),
    351: ("LeaflessTree", "leafless_tree"),
    411: ("TimberTree", "timber_tree"),
    413: ("PineTree", "pine_tree"),
    414: ("JungleTree", "jungle_tree"),
    1341: ("CarbonRock", "carbon_rock"),
}

# key: index; value: (nyan object name, filename prefix, units belonging to group, variant type)
VARIANT_GROUP_LOOKUPS = {
    69: ("Shorefish", "shore_fish", (69,), "misc"),
    96: ("Bird", "bird", (96, 816), "misc"),
    264: ("Cliff", "cliff", (264, 265, 266, 267, 268, 269, 270, 271, 272, 273), "angle"),
    450: ("BigOceanFish", "big_ocean_fish", (450, 451), "random"),
    455: ("OceanFish", "ocean_fish", (455, 456, 457, 458), "random"),
}

# key: head unit id; value: (nyan object name, filename prefix)
TECH_GROUP_LOOKUPS = {
    1: ("TechLevel2", "tech_level_2"),
    2: ("TechLevel3", "tech_level_3"),
    3: ("TechLevel4", "tech_level_4"),
    9: ("AWing", "a_wing"),
    33: ("BasicTraining", "basic_training"),
    61: ("HolonetTranceiver", "holonet_traceiver"),
    62: ("BothanSpynet", "bothan_spynet"),
    63: ("ElevationTracking", "elevation_tracking"),
    64: ("LightPlating", "light_plating"),
    65: ("BasicArmor", "basic_armor"),
    66: ("PrimaryFocusingCoils", "primary_focusing_coils"),
    67: ("HeavyArmor", "heavy_armor"),
    68: ("HeavyPlating", "heavy_plating"),
    69: ("TargetingSensors", "targeting_sensors"),
    70: ("EnlargedBombHold", "enlarged_bomb_hold"),
    71: ("FlightSchool", "flight_school"),
    72: ("EfficientManufactering", "efficient_manufacturing"),
    73: ("ShieldModifications", "shield_modifications"),
    74: ("CoolingSleeves", "cooling_sleeves"),
    75: ("ExternalSensorPod", "external_sensor_pod"),
    76: ("AdvancedFlightSchool", "advanced_flight_school"),
    77: ("ArmoredPlates", "armored_plates"),
    78: ("AdvancedEngines", "advanced_engines"),
    79: ("LightArmor", "light_armor"),
    80: ("MediumPlating", "medium_plating"),
    81: ("AdvancedPowerPack", "advanced_power_pack"),
    82: ("Stimulants", "stimulants"),
    83: ("Genetics", "genetics"),
    126: ("SensorBeacon", "sensor_beacon"),
    127: ("Cloning", "cloning"),
    128: ("UpgradedMotivator", "upgraded_motivator"),
    129: ("OptimizedMotivator", "optimized_motivator"),
    130: ("SensorArray", "sensor_array"),
    132: ("FusionExtractor", "fusion_extractor"),
    133: ("SelfRegeneraration", "self_regeneration"),
    134: ("Irrigation", "irrigation"),
    135: ("HarvestingProgram", "harvesting_program"),
    136: ("AdvancedHarvestingProgram", "advanced_harvesting_program"),
    137: ("BattleArmor", "battle_armor"),
    138: ("Taxation", "taxation"),
    139: ("AttackProgramming", "attack_programming"),
    141: ("Presidium", "presidium"),
    143: ("BerserkerJetPacks", "berserker_jet_packs"),
    144: ("StrengthenedFrame", "strengthened_frame"),
    145: ("CreatureTraining", "creature_training"),
    146: ("ReinforcedFrame", "reinforced_frame"),
    147: ("Mechanics", "mechanics"),
    148: ("ForestVision", "forest_vision"),
    149: ("NovaBeamdrillMining", "beamdrill_mining_nova"),
    150: ("HeavyNovaBeamdrill", "heavy_beamdrill_nova"),
    151: ("ForceAgility", "force_agility"),
    152: ("ForceConcentration", "force_concentration"),
    153: ("ForceRegeneration", "force_regeneration"),
    154: ("ForceStrength", "force_strength"),
    155: ("FaithInTheForce", "faith_in_the_force"),
    156: ("ForceInfluence", "force_influence"),
    157: ("ForceStrong", "force_strong"),
    158: ("ForcePerception", "force_perception"),
    159: ("JediMindTrick", "jedi_mind_trick"),
    160: ("HandheldCarbonExtractor", "handheld_carbon_extractor"),
    161: ("EnhancedCarbonExtractor", "enhanced_carbon_extractor"),
    162: ("HeavyCarbonExtractor", "heavy_carbon_extractor"),
    163: ("UpgradedGenerator", "upgraded_generator"),
    164: ("WalkerResearch", "walker_research"),
    166: ("AdvancedGenerator", "advanced_generator"),
    167: ("GunganCreatureArmor", "gungan_creature_armor"),
    168: ("AdvancedRedesign", "advanced_redesign"),
    169: ("WookieIngenuity", "wookie_ingenuity"),
    170: ("Technicians", "technicians"),
    171: ("OreBeamdrillMining", "beamdrill_mining_ore"),
    172: ("HeavyOreBeamdrill", "heavy_beamdrill_ore"),
    173: ("Durasteel", "durasteel"),
    174: ("IonAccelerator", "ion_accelerator"),
    175: ("PowerCalibrator", "power_calibrator"),
    176: ("RotationBearings", "rotation_bearings"),
    177: ("TrackingComputer", "tracking_computer"),
    178: ("HomingSensors", "homing_sensors"),
    179: ("HeavyWeaponsEngineers", "heavy_weapons_engineers"),
    180: ("PermacitePlating", "permacite_plating"),
    183: ("RedesignedSpecs", "redesigned_specs"),
    184: ("AdvancedPropulsion", "advanced_propulsion"),
    185: ("RedoubledEfforts", "redoubled_efforts"),
    186: ("AdvancedScanning", "advanced_scanning"),
    187: ("FasterGrowthChambers", "faster_growth_chambers"),
    188: ("HuttEndorsement", "hutt_endorsement"),
    189: ("NeimoidianEndorsement", "neimoidian_endorsement"),
    190: ("GalacticBanking", "galactic_banking"),
    192: ("InsiderTrading", "insider_trading"),
    193: ("GalacticTradeComission", "galactic_trade_comission"),
    194: ("AlteredBargains", "altered_bargains"),
    195: ("MarketControl", "market_control"),
    196: ("DroidAssistents", "droid_assistents"),
    197: ("MacroBinoculars", "macro_binoculars"),
    198: ("LighterArmor", "lighter_armor"),
    199: ("PortableScanner", "portable_scanner"),
    201: ("FarSeeInBinoculars", "far_see_in_binoculars"),
    202: ("IncreasedExplosiveYields", "increased_explosive_yields"),
    203: ("IntegratedRangefinder", "integrated_rangefinder"),
    204: ("TougherArmor", "tougher_armor"),
    205: ("Dexterity", "dexterity"),
    206: ("AutomatedProcesses", "automated_processes"),
    500: ("ForceMeditation", "force_meditation"),
    501: ("BactaTanks", "bacta_tanks"),
    535: ("GrenadierTraining", "grenadier_training"),
    568: ("AntiAirRetrofit", "anti_air_retrofit"),
    569: ("StrenghtenedSuperstructure", "streghtened_superstructure"),
    570: ("SuperconductingShields", "superconducting_shields"),
    571: ("EfficientBuildings", "efficient_buildings"),
    572: ("PowerCoreShielding", "power_core_shielding"),
    573: ("StregthenedAssembly", "strengthened_assembly"),
    574: ("GeonosianDiligence", "geonosian_diligence"),
    575: ("ConfederacyAlliance", "confederacy_alliance"),
    576: ("GeonosianEngineers", "geonosian_engineers"),
    577: ("DroidUpgrades", "droid_upgrades"),
    578: ("UpgradedMedicDroids", "upgraded_medic_droids"),
    579: ("KaminoanCloners", "kaminoan_cloners"),
    580: ("GalacticSenateHub", "galactic_senate_hub"),
    581: ("SightBeyondSight", "sight_beyond_sight"),
    582: ("KaminoanRefit", "kaminoan_refit"),
    583: ("AirCruiserBoost", "air_cruiser_boost"),

    # Unit/Building upgrades
    113: ("HeavyDestroyerDroid", "heavy_destroyer_droid"),
    222: ("ForceKnightUpgrade", "force_knight_upgrade"),
    236: ("AdvancedMountedTrooper", "advanced_mounted_trooper"),
    241: ("HeavyMountedTrooper", "heavy_mounted_trooper"),
    246: ("HeavyAntiAirTrooper", "heavy_anti_air_trooper"),
    251: ("HeavyTrooper", "heavy_trooper"),
    256: ("RepeaterTrooper", "repeater_trooper"),
    261: ("TrooperUpgrade", "trooper_upgrade"),
    301: ("HeavyStrikeMech", "heavy_strike_mech"),
    306: ("HeavyMechDestroyer", "heavy_mech_destroyer"),
    311: ("HeavyAssaultMech", "heavy_assault_mech"),
    331: ("HeavyArtillery", "heavy_artillery"),
    341: ("HeavyAntiAirMobile", "heavy_anti_air_mobile"),
    346: ("HeavyPummel", "heavy_pummel"),
    366: ("AdvancedBomber", "advanced_bomber"),
    371: ("AdvancedFighter", "advanced_fighter"),
    376: ("EnhancedBomber", "enhanced_bomber"),
    381: ("FastFighter", "fast_fighter"),
    416: ("AdvancedFrigate", "advanced_frigate"),
    421: ("AdvancedCruiser", "advanced_cruiser"),
    426: ("HeavyDestroyer", "heavy_destroyer"),
    431: ("HeavyAntiAirDestroyer", "heavy_anti_air_destroyer"),
    441: ("FrigateUpgrade", "frigate_upgrade"),
    447: ("AdvancedAntiAirTurret", "advanced_anti_air_turret"),
    472: ("DarkTrooperPhase2", "dark_trooper_phase2"),
    474: ("HeavyFambaaShieldGenerator", "heavy_fambaa_shield_generator"),
    475: ("EliteRoyalCrusader", "elite_royal_crusader"),
    476: ("ArmoredAirspeeder", "armored_airspeeder"),
    477: ("AdvancedBerserker", "advanced_berserker"),
    480: ("MediumTurret", "medium_turret"),
    481: ("AdvancedTurret", "advanced_turret"),
    483: ("HeavyWall", "heavy_wall"),
    484: ("ShieldWall", "shield_wall"),
    533: ("AdvancedJediStarfighter", "advanced_jedi_starfighter"),
    534: ("EliteGeonosianWarrior", "elite_geonosian_warrior"),
}

# key: tech id; value: (tech ids of civ line unlocks/upgrades)
CIV_TECH_ASSOCS = {
    222: (93, 222, 223, 224, 225, 226, 544, 545),
    236: (86, 236, 237, 238, 239, 240, 556, 557),
    241: (55, 241, 242, 243, 244, 245, 558, 559),
    246: (45, 246, 247, 248, 249, 250, 560, 561),
    251: (44, 251, 252, 253, 254, 255, 562, 563),
    256: (90, 256, 257, 258, 259, 260, 564, 565),
    261: (43, 261, 262, 263, 264, 265, 566, 567),
    301: (42, 301, 302, 303, 304, 305, 550, 551),
    306: (40, 306, 307, 308, 309, 310, 552, 553),
    311: (99, 311, 312, 313, 314, 315, 554, 555),
    331: (57, 331, 332, 333, 334, 335, 538, 539),
    341: (56, 341, 342, 343, 344, 345, 540, 541),
    346: (59, 346, 347, 348, 349, 350, 542, 543),
    366: (108, 366, 367, 368, 369, 370, 517, 518),
    371: (110, 371, 372, 373, 374, 375, 519, 520),
    376: (107, 376, 377, 378, 379, 380, 521, 522),
    381: (109, 381, 382, 383, 384, 385, 523, 524),
    416: (53, 416, 417, 418, 419, 420, 508, 537),
    421: (51, 421, 422, 423, 424, 425, 509, 510),
    426: (50, 426, 427, 428, 429, 430, 511, 512),
    431: (49, 431, 432, 433, 434, 435, 513, 514),
    441: (52, 441, 442, 443, 444, 445, 515, 516),
}

# key: civ index; value: (nyan object name, filename prefix)
CIV_GROUP_LOOKUPS = {
    0: ("Nature", "nature"),
    1: ("Empire", "empire"),
    2: ("Gungans", "gungans"),
    3: ("Rebels", "rebels"),
    4: ("Naboo", "naboo"),
    5: ("Federation", "federation"),
    6: ("Wookiees", "wokiees"),
    7: ("Republic", "republic"),
    8: ("Confederacy", "confederacy"),
}

# key: civ index; value: (civ ids, nyan object name, filename prefix)
GRAPHICS_SET_LOOKUPS = {
    0: ((0, 1), "Empire", "empire"),
    1: ((2,), "Gungan", "gungan"),
    2: ((3,), "Rebel", "rebel"),
    3: ((4,), "Naboo", "naboo"),
    4: ((5,), "Federation", "federate"),
    5: ((6,), "Wookiee", "wookiee"),
    6: ((7,), "Republican", "reppublican"),
    7: ((8,), "Confederate", "confederate"),
}

# key: terrain index; value: (unit terrain restrictions (manual), nyan object name, filename prefix)
# TODO: Use terrain restrictions from .dat
TERRAIN_GROUP_LOOKUPS = {
    0:  ((0,), "Grass0", "grass0"),
    1:  ((0,), "Water0", "water0"),
    2:  ((0,), "Shore", "shore"),
    3:  ((0,), "Dirt0", "dirt0"),
    4:  ((0,), "Swamp", "swamp"),
    5:  ((0,), "Leaves", "leaves"),
    6:  ((0,), "Dirt1", "dirt1"),
    7:  ((0,), "FarmCrops", "farm_crops"),
    8:  ((0,), "FarmHarvested", "farm_harvested"),
    9:  ((0,), "Grass1", "grass1"),
    10: ((0,), "Forest0", "forest0"),
    11: ((0,), "Dirt2", "dirt2"),
    12: ((0,), "Grass2", "grass2"),
    13: ((0,), "Forest1", "forest1"),
    14: ((0,), "Sand", "sand"),
    15: ((0,), "Clouds", "clouds"),
    16: ((0,), "Space", "space"),
    17: ((0,), "Forest2", "forest2"),
    18: ((0,), "Forest3", "forest3"),
    19: ((0,), "Forest4", "forest4"),
    20: ((0,), "Forest5", "forest5"),
    21: ((0,), "Forest6", "forest6"),
    22: ((0,), "Water1", "water1"),
    23: ((0,), "Water2", "water2"),
    24: ((0,), "Path0", "path0"),
    25: ((0,), "Path1", "path1"),
    26: ((0,), "Desert0", "desert0"),
    27: ((0,), "Foundation", "foundation"),
    28: ((0,), "Water3", "water3"),
    29: ((0,), "FarmConstruction3", "farm_construction3"),
    30: ((0,), "FarmConstruction2", "farm_construction2"),
    31: ((0,), "FarmConstruction1", "farm_construction1"),
    32: ((0,), "Snow0", "snow0"),
    33: ((0,), "Snow1", "snow1"),
    34: ((0,), "Snow2", "snow2"),
    35: ((0,), "Ice", "ice"),
    36: ((0,), "FoundationSnow", "foundation_snow"),
    37: ((0,), "ShoreIce", "shore_ice"),
    38: ((0,), "Path2", "path2"),
    39: ((0,), "Path3", "path3"),
    40: ((0,), "Path4", "path4"),
    41: ((0,), "Grass3", "grass3"),
    42: ((0,), "Rock0", "rock0"),
    43: ((0,), "Metal", "metal"),
    44: ((0,), "Rock1", "rock1"),
    45: ((0,), "Desert1", "desert1"),
    46: ((0,), "Desert2", "desert2"),
    47: ((0,), "Snow3", "snow3"),
    48: ((0,), "FarmGreen", "farm_green"),
    49: ((0,), "MetalCarb", "metal_carb"),
    50: ((0,), "Rock2", "rock2"),
    51: ((0,), "Lava", "lava"),
    52: ((0,), "Rock3", "rock3"),
}

# key: not relevant; value: (terrain indices, unit terrain restrictions (manual), nyan object name)
# TODO: Use terrain restrictions from .dat
TERRAIN_TYPE_LOOKUPS = {
}

CLASS_ID_LOOKUPS = {
    1: "Bantha",
    2: "Fambaa",
    4: "Animal",
    5: "Monster",
    6: "Wall",
    7: "Farm",
    8: "Gate",
    9: "AntiAirTurret",
    10: "LaserTurret",
    11: "Cruiser",
    13: "Destroyer",
    14: "UtilityTrawler",
    15: "Frigate",
    16: "AntiAirDestroyer",
    17: "TransportShip",
    18: "BuildingMisc",
    19: "Doppelganger",
    20: "DeadOrProjectile",     # do not use this as GameEntityType
    21: "HOLDTHIS",             # something from a scenario
    22: "Cliff",
    23: "OceanFish",
    25: "ShoreFish",
    26: "AmbientFlag",
    27: "BerryBush",
    28: "Holocron",
    29: "Nova",
    30: "Ore",
    31: "CarbonTree",
    32: "Artillery",
    33: "AntiAirMobile",
    34: "MobileCannon",
    35: "Pummel",
    36: "Cannon",
    39: "UnderwaterFrigate",
    40: "AntiAirDestroyer",
    42: "EyeCandy",
    43: "Bomber",
    44: "BountyHunter",
    45: "CargoHovercraft",
    46: "ScenarioUnit",                  # should not be present in final modpack
    47: "Scout",
    48: "Fighter",
    49: "GrenadeTrooper",
    50: "Jedi",
    51: "JediWithHolocron",
    52: "Trooper",
    53: "Mech",
    54: "Medic",
    55: "AntiAirTrooper",
    56: "MountedTrooper",
    57: "FambaaShieldGenerator",
    58: "Worker",
    59: "AirTransport",
    60: "Herdable",
    61: "PowerDroid",
    62: "AirCruiser",
    63: "GeonosianWarrior",
    64: "JediStarfighter",
}

# key: genie unit id; value: Gather ability name
GATHER_TASK_LOOKUPS = {
    13: ("Fish", "fish"),  # fishing ship
    56: ("Fish", "fish"),
    57: ("Fish", "fish"),
    120: ("CollectBerries", "collect_berries"),
    354: ("CollectBerries", "collect_berries"),
    122: ("HarvestGame", "harvest_game"),
    216: ("HarvestGame", "harvest_game"),
    123: ("ExtractCarbon", "extract_carbon"),
    218: ("ExtractCarbon", "extract_carbon"),
    124: ("MineOre", "mine_ore"),
    220: ("MineOre", "mine_ore"),
    214: ("FarmCrops", "farm_crops"),
    259: ("FarmCrops", "farm_crops"),
    579: ("MineNova", "mine_nova"),
    581: ("MineNova", "mine_nova"),
    590: ("HarvestLivestock", "harvest_livestock"),
    592: ("HarvestLivestock", "harvest_livestock"),
}

# key: restock target unit id; value: Gather ability name
RESTOCK_TARGET_LOOKUPS = {
    50: ("ReseedFarm", "reseed_farm"),
}

# key: armor class; value: Gather ability name
ARMOR_CLASS_LOOKUPS = {
    0: "AirCraft",
    1: "HeavyAssaultMech",
    2: "HeavyMech",
    3: "Energy",
    4: "Melee",
    5: "ForceUnit",
    6: "AssaultMachine",
    7: "Decimator",
    8: "Shielded",
    9: "Ship",
    10: "Submarine",
    11: "Building",
    13: "DefenseBuilding",
    14: "Trooper",
    15: "MountedTrooper",
    16: "Cruiser",
    17: "HeavySiege",
    19: "Worker",
    20: "Destroyer",
    21: "StandardBuilding",
    22: "Wall",
    23: "AirCruiser",
    24: "WildAnimal",
    26: "Fortress",
    30: "TameAnimal",
}

# key: command type; value: Apply*Effect ability name
COMMAND_TYPE_LOOKUPS = {
    7: ("Attack", "attack"),
    101: ("Construct", "construct"),
    104: ("Convert", "convert"),
    105: ("Heal", "heal"),
    106: ("Repair", "repair"),
    110: ("Hunt", "hunt"),
}


import toml
import re
import os.path

def load(filename, **kwargs):
    with open(filename, 'r') as fp:
        return loads(fp.read(), root_dir=os.path.dirname(filename), **kwargs)

def loads(s, **kwargs):
    parser = ConfigParser(s, **kwargs)
    return parser.config

def expand_variable(config):
    parser = ConfigParser(None)
    parser.config = config
    parser.expand()
    return parser.config

def find_include_file(filename, root_dir = None):
    if root_dir is not None and os.path.exists(os.path.join(root_dir, filename)):
        return os.path.join(root_dir, filename)
    elif os.path.exists(filename):
        return filename
    else:
        raise AttributeError('Including file is not found: ' + filename)

def expand_include(config, root_dir = None):
    if 'include' not in config['config']:
        return config
    includes = config['config']['include']
    if not isinstance(includes, list):
        includes = [ includes ]

    included_config = {}

    load_configs = [ ConfigParser(find_include_file(include, root_dir), is_variable_expansion=False).config for include in includes ]

    load_configs.append(config)
    for including_config in load_configs:
        for key in including_config:
            if isinstance(including_config[key], list):
                included_config.setdefault(key, []).extend(including_config[key])
            elif isinstance(including_config[key], dict):
                included_config.setdefault(key, {}).extend(including_config[key])
            else:
                included_config[key] = including_config[key]

    return included_config

class ConfigParser:
    def __init__(self, config_string, is_variable_expansion = True, root_dir=None):
        self.variable_pattern = re.compile(r'\$\{?(?P<variable>[\w\d_\.]+)\}?')
        if config_string is None:
            self.config = None
            return

        self.config = toml.loads(config_string)

        self.config = expand_include(self.config, root_dir)
        if is_variable_expansion:
            self.expand()

    def expand(self):
        for key, value in self.config.items():
            self.config[key] = self.recursive_apply_variables(value)

    def try_cast(self, value, src_value):
        if isinstance(src_value, (float, int)):
            try:
                t = type(src_value)
                return t(value)
            except:
                pass

        return value

    def recursive_apply_variables(self, field):
        if isinstance(field, dict):
            for key, value in field.items():
                field[key] = self.recursive_apply_variables(value)
        elif isinstance(field, list):
            for i in range(len(field)):
                field[i] = self.recursive_apply_variables(field[i])
        else:
            field = self.apply_variables(field)
        return field

    def apply_variables(self, value):
        if not isinstance(value, str):
            return value
        matches = re.findall(self.variable_pattern, value)
        for match in matches:
            value = self.replace_variable(value, match)
        return value

    def get_value(self, dict_, var_key):
        keys = var_key.split('.')
        for key in keys:
            if key not in dict_:
                raise KeyError('Unknown variable: {}'.format(var_key))
            dict_ = dict_[key]
        return dict_

    def replace_variable(self, value, var_name):
        var_value = self.get_value(self.config, var_name)
        assert not isinstance(var_value, dict), 'Currently, not supported dict variable expansion.'
        if isinstance(var_value, list):
            result = []
            for v in var_value:
                assert not isinstance(v, list)
                replaced_v = value.replace('${' + var_name + '}', str(v))
                result.append(self.try_cast(replaced_v, v))
            return result
        else:
            replaced_value = value.replace('${' + var_name + '}', str(var_value))
            return self.try_cast(replaced_value, var_value)

    def check_variable(self, string):
        if not isinstance(string, str):
            return None

        matched = re.findall(self.variable_pattern, string)
        if len(matched) == 0:
            return None

        return matched


#!/usr/bin/env python

from pwd import getpwnam
from grp import getgrnam

def set_paths_based_on_ossec(o_path='/var/ossec'):
    """
    Set paths based on ossec location.
    :param o_path: OSSEC Path, by default it is '/var/ossec'.
    :return:
    """

    global ossec_path
    ossec_path = o_path

    global ossec_conf
    ossec_conf = "{0}/etc/ossec.conf".format(ossec_path)

    global internal_options
    internal_options = "{0}/etc/internal_options.conf".format(ossec_path)
    global local_internal_options
    local_internal_options = "{0}/etc/local_internal_options.conf".format(ossec_path)

    global ossec_log
    ossec_log = "{0}/logs/ossec.log".format(ossec_path)

    global client_keys
    client_keys = '{0}/etc/client.keys'.format(ossec_path)

    global stats_path
    stats_path = '{0}/stats'.format(ossec_path)

    global ruleset_path
    # ruleset_path = '{0}/rules'.format(ossec_path)
    ruleset_path = '{0}'.format(ossec_path)

    global groups_path
    groups_path = "{0}/queue/agent-groups".format(ossec_path)

    global shared_path
    shared_path = "{0}/etc/shared".format(ossec_path)

    global backup_path
    backup_path = "{0}/backup".format(ossec_path)

    global ruleset_rules_path
    ruleset_rules_path = '{0}/rules'.format(ruleset_path)

    global database_path
    database_path = "mongodb://127.0.0.1:27017"

    global global_db
    global_db = 'global'

    global wdb_socket_path
    wdb_socket_path = '{0}/queue/db/wdb'.format(ossec_path)

    global api_config_path
    api_config_path = "{0}/api/configuration/config.js".format(ossec_path)

    global default_decoder_xml
    default_decoder_xml = "{0}/etc/decoder.xml".format(ossec_path)

    global custom_decoder_xml
    custom_decoder_xml = "{0}/etc/local_decoder.xml".format(ossec_path)

    # global database_path_agents
    # database_path_agents = database_path + '/agents'

    global os_pidfile
    os_pidfile = "/var/run"

    # Queues
    global ARQUEUE
    ARQUEUE = "{0}/queue/alerts/ar".format(ossec_path)

    global EXECQ
    EXECQ = "{0}/queue/alerts/execq".format(ossec_path)

    # Socket
    global AUTHD_SOCKET
    AUTHD_SOCKET = "{0}/queue/ossec/auth".format(ossec_path)

# Agent upgrading variables
wpk_repo_url = "packages.wazuh.com/wpk/"

wpk_chunk_size = 512

open_retries = 10 # Retries until get open ok message
open_sleep = 5 # Seconds between retries

upgrade_result_retries = 60 # Retries until get upgrade_result ok message
upgrade_result_sleep = 5 # Seconds between retries

agent_info_retries = 100 # Retries to detect when agent_info file is updated
agent_info_sleep = 2 # Seconds between retries

# Common variables
database_limit = 500
maximum_database_limit = 1000
limit_seconds = 1800 # 600*3

ossec_uid = getpwnam("ossec").pw_uid
ossec_gid = getgrnam("ossec").gr_gid

# Common variables based on ossec path (/var/ossec by default)
set_paths_based_on_ossec()


import uuid
from django.db import models

from .timestamped import TimestampedModel
from .emprestimo import Emprestimo
from .abono import Abono

class Suspensao(TimestampedModel):
    _id = models.UUIDField(
        unique=True,
        editable=False,
        default=uuid.uuid4
    )
    emprestimo = models.ForeignKey(
        to=Emprestimo,
        on_delete=models.PROTECT,
        related_name='suspensoes'
    )
    abono = models.ForeignKey(
        to=Abono,
        on_delete=models.PROTECT,
        related_name='suspensoes',
        null=True
    )
    usuario_id = models.UUIDField()
    total_dias = models.PositiveIntegerField()

    class Meta:
        db_table = 'suspensoes'


import os
import tempfile
import sqlite3
from lib.Tools import *
from lib.urlParser import Parse
from lib.report import Report
from lib.db import db_insert

class Controller:
    def __init__(self, arguments):
        self.urls_target = arguments.urlList
        self.domains_target = arguments.domainList
        self.logfile = tempfile.NamedTemporaryFile(delete=False).name
        self.log = {}
        self.xray = Xray()

        if arguments.tools:
            self.toolsList = [tool for tool in arguments.tools.split(',')]

    def assign_task(self):
        def url_scan(urls_target):
            if urls_target:
                self.urls_target = sorted(set(self.urls_target), key=urls_target.index)
                for _target in urls_target:
                    db_insert('insert into target_info (target, batch_num) values (?,?);', _target, now_time)
                    _target = Parse(_target)                  # return dict{ip,domain,http_url}
                    if _target.data:
                        db_insert('insert into scanned_info (domain, batch_num) values (?,?)', _target.data['http_url'], now_time)
                        self.urls_scan(_target)
                    Report().html_report_single()

        self.xray.passive_scan()

        if self.urls_target:
            url_scan(self.urls_target)

        if self.domains_target:
            self.domains_target = sorted(set(self.domains_target), key=self.domains_target.index)
            for domain in self.domains_target:
                if not Parse.isIP(domain):
                    if Parse(domain).data:      # 域名存在解析不成功的情况
                        subdomains = self.subdomains_scan(Parse(domain).data['domain'])
                        for subdomain in subdomains:
                            target = Parse(subdomain)
                            print(target)
                            if target.data:
                                db_insert('insert into host_info (domain, batch_num) values (?,?)', target.data['domain'], now_time)
                                http_urls = self.ports_scan(target)
                                url_scan(http_urls)

                else:
                    target = Parse(domain)
                    db_insert('insert into host_info (domain, batch_num) values (?,?)', target.data['domain'], now_time)
                    http_urls = self.ports_scan(target)
                    url_scan(http_urls)

    def subdomains_scan(self, target):
        _ = "python3 oneforall/oneforall.py --target {target} run".format(path=tool_path, target=target)
        logfile = '{path}/oneforall/results/{target}.csv'.format(path=tool_path, target=target)
        oneforall = Oneforall(_, logfile)
        return oneforall.data if oneforall.data else [target]

    def ports_scan(self, target):
        nslookup = Nslookup(target.data['domain'])

        cdns = ['cdn', 'kunlun', 'bsclink.cn', 'ccgslb.com.cn', 'dwion.com', 'dnsv1.com', 'wsdvs.com', 'wsglb0.com', 'lxdns.com', 'chinacache.net.', 'ccgslb.com.cn', 'aliyun']
        for cdn in cdns:
            if cdn in nslookup.log:
                print('maybe the {} is cdn'.format(target.data['domain']))
                print(nslookup.log)
                return [target.data['http_url']]

        _ = "masscan --open -sS -Pn -p 1-20000 {target} --rate 2000".format(target=target.data['ip'])
        masscan = Masscan(_, None)

        '''
        可能存在防火墙等设备,导致扫出的端口非常多
        '''
        if not masscan.data or len(masscan.data) > 20:
            masscan.data = ['21', '22', '445', '80', '1433', '3306', '3389', '6379', '7001', '8080']

        '''
        nmap 如果80和443同时开放，舍弃443端口
        '''
        _ = "nmap -sS -Pn -A -p {ports} {target_ip} -oN {logfile}".format(ports=','.join(masscan.data), target_ip=target.data['ip'], logfile=self.logfile)
        nmap = Nmap(_, self.logfile)
        if nmap.data:
            if '80' in nmap.data and '443' in nmap.data:
                nmap.data.remove("443")
            return ['{}:{}'.format(target.data['http_url'], port) for port in nmap.data]

        return [target.data['http_url']]

    def urls_scan(self, target):
        # 主要查看组织， 是否是云服务器
        iplocation = IpLocation(target.data['ip'])

        _ = "whatweb --color never {}".format(target.data['http_url'])
        whatweb = Whatweb(_)

        # 截图
        snapshot = Snapshot(target.data['http_url'])

        '''
        crawlergo
        扫描出来的子域名动态添加到域名列表中
        注意--push-to-proxy必须是http协议, 更换chrome为静态包执行不了
        '''
        _ = './crawlergo -c /usr/bin/google-chrome-stable -t 10 --push-to-proxy http://127.0.0.1:7777 -o json {}'.format(target.data['http_url'])
        crawlergo = Crawlergo(_)

        if crawlergo.data:
            if self.domains_target:
                self.domains_target += [domain for domain in crawlergo.data if domain not in self.domains_target]

        '''
        等待xray扫描结束，因为各类工具都是多线程高并发，不等待的话xray会一批红：timeout
        '''
        if crawlergo.log:
            print('xray start scanning')
            while True:
                if self.xray.wait_xray_ok():
                    break

        '''
        将dirsearch扫出的url添加到xray去
        '''
        _ = 'python3 dirsearch/dirsearch.py -x 301,302,403,404,405,500,501,502,503 --full-url -u {target} --csv-report {logfile}'.format(
                    target=target.data['http_url'], logfile=self.logfile)
        dirsearch = Dirsearch(_, self.logfile)

        if dirsearch.data:
            for url in dirsearch.data:
                response = Request().repeat(url)


import tempfile
import time

import math

from io import BufferedWriter, FileIO

import numpy as np
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render

# Create your views here.
from django.views.decorators.csrf import csrf_exempt

from helpers.fb.scraper import get_fb_email, get_fb_name, get_profile_pic
from helpers.matching.similarity import get_most_similar_star
from helpers.model.user import create_plain_user
from helpers.msm_analysis.msm_calc import get_mia_san_mia_photo_score, get_mia_san_mia_photo_score_local
from helpers.sap.user_managment import get_user, update_user

active_users = {}
name_list_orig = list(np.random.permutation(["Blondell Fegley","Cassi Friese","Edith Zahler","Roscoe Saidi","Efrain Colley","Edyth Bakke","Gerardo Millhouse","Loise Murtha","Akiko Lazzaro","Dusti Justice","Beryl Gillett","Noriko Fielden","Eleonor Creagh","Elanor Lebo","Antonetta Wemmer","Vergie Spikes","Veola Corpus","Marya Eckard","Yuko Sieck","Corina Kamaka"]))
name_list = list(np.random.permutation(["Blondell Fegley","Cassi Friese","Edith Zahler","Roscoe Saidi","Efrain Colley","Edyth Bakke","Gerardo Millhouse","Loise Murtha","Akiko Lazzaro","Dusti Justice","Beryl Gillett","Noriko Fielden","Eleonor Creagh","Elanor Lebo","Antonetta Wemmer","Vergie Spikes","Veola Corpus","Marya Eckard","Yuko Sieck","Corina Kamaka"]))

def user_login(request):
    # print(request.readlines())
    if request.method == 'GET':
        if len(request.GET.keys()) > 0:
            if "token" in request.GET.keys():

                try:
                    f_auth = request.GET['token']

                    email = get_fb_email(id="me", access_token=f_auth)
                    user, u_pid = get_user(email)
                    user["fb_token"] = f_auth

                    if user["profile_id"] == "":
                        name = get_fb_name(id="me", access_token=f_auth)
                        profile_pic = get_profile_pic(id="me", access_token=f_auth)

                        sim_star, img_url, f_score, uni_s = get_most_similar_star(f_auth)

                        user["profile_id"] = u_pid
                        user["name"] = name
                        user["profile_pic"] = profile_pic
                        user["star"]["star_name"] = sim_star
                        user["star"]["face_sim"] = f_score
                        user["star"]["post_sim"] = uni_s
                        user["star"]["url"] = img_url

                        import numpy as np
                        user["skills"]["motivation"] = np.random.randint(0, 15)
                        user["skills"]["tactics"] = np.random.randint(0, 15)
                        user["skills"]["endurance"] = np.random.randint(0, 15)
                        user["skills"]["teamplay"] = np.random.randint(0, 15)
                        user["skills"]["style"] = np.random.randint(0, 15)

                        print("Updated")

                        update_user(user, u_pid)

                    # name_list = list(np.random.permutation(name_list_orig))
                    # i = np.random.randint(2, 6)
                    # name_list[i] = name

                    update_user({"fb_token": f_auth}, u_pid)

                    print(user)

                    return JsonResponse(user)

                except:
                    return JsonResponse(create_plain_user())

            else:
                return HttpResponse(";-P")

                # f_auth = "".join(request.GET.keys())
                #
                # return HttpResponse("Most Similar Star: " + sim_star + " .

 Face matching: "+ str(f_score) +"

Common Stuff " + " , ".join(uni_s))
                # return JsonResponse({'name': sim_star, 'img_url' : img_url, 'f_score': f_score, 'uni_s' : list(uni_s)})
        else:
            return HttpResponse(":)")
            #    return HttpResponse("Hello, world. You're at the polls index.")

def location(request):
    if request.method == 'GET':
        if len(request.GET.keys()) > 0:
            if "token" in request.GET.keys() and "lat" in request.GET.keys() and "lng" in request.GET.keys():
                try:

                    print(active_users)

                    f_auth = request.GET['token']
                    lat = request.GET['lat']
                    lng = request.GET['lng']

                    email = get_fb_email(id="me", access_token=f_auth)
                    user, _ = get_user(email)

                    nearby = []

                    active_users[f_auth] = (time.time(), lat, lng, user)

                    for f_id, x in active_users.items():
                        if x:
                            u_time, u_lat, u_lng, u_user = x

                            if u_user.name == user.name:
                                continue

                            if time.time() - u_time > 60 * 10:
                                active_users[f_id] = None
                                continue

                            if True or math.sqrt((u_lat - lat) ** 2 + (u_lng - lng) ** 2) < 0.1:
                                nearby.append(u_user.name)

                    return JsonResponse({"nearby" : nearby})

                except:
                    return JsonResponse({"nearby" : nearby})
            else:
                return JsonResponse({"nearby": ["John Wanye", "Paulchen Panther"]})
        else:
            return JsonResponse({"nearby": ["John Wanye", "Paulchen Panther"]})
    else:
        return JsonResponse({"nearby": ["John Wanye", "Paulchen Panther"]})

    return None

def msmness(request):
    if request.method == 'GET':
        if len(request.GET.keys()) > 0:
            if "url" in request.GET.keys():
                url = request.GET['url']
                try:
                    msm_score = get_mia_san_mia_photo_score(url)
                except:
                    print("Random!!!!")
                    import random
                    msm_score = random.uniform(0, 1)
                return JsonResponse({"score": msm_score})
            else:
                return HttpResponse("0o")
        else:
            return HttpResponse("0o")
    else:
        return HttpResponse("0o")

    return None

def ranking(request):
    return JsonResponse({"ranking": name_list})

@csrf_exempt
def msm_image(request):
    try:

        id = request.POST['id']
        upload = request.FILES['file']

        fh = tempfile.NamedTemporaryFile(delete=False)
        extension = upload.name.split(".")[1]
        filename = "{}.{}".format(fh.name, extension)

        print(filename)

        with BufferedWriter(FileIO(filename, "w")) as dest:
            for c in upload.chunks():
                dest.write(c)

        msm_score, s_hap, s_face, s_red = get_mia_san_mia_photo_score_local(filename)

        print("Img recieved")

        return JsonResponse({"score": msm_score, "happiness": s_hap, "faces": s_face, "redness": s_red})

    except:

        print("Random!!!!")
        import np.random
        msm_score = np.random.randrange(0, 101)
        s_hap = np.random.randrange(0, 11)
        s_face = np.random.randrange(0, 11)
        s_red = np.random.randrange(0, 11)

        return JsonResponse({"score": msm_score, "happiness": s_hap, "faces": s_face, "redness": s_red})


from pyspark.sql import SparkSession
spark = SparkSession.builder.master("local[1]").appName("SparkByExamples.com").getOrCreate()
address = [(1,"14851 Jeffrey Rd","DE"),
    (2,"43421 Margarita St","NY"),
    (3,"13111 Siemon Ave","CA")]
df =spark.createDataFrame(address,["id","address","state"])
df.show()

#1. PySpark Replace string column values using withColumn() where first value is the column we are working on

from pyspark.sql.functions import *

df.withColumn("address",regexp_replace("address","Rd","Road")).show()

#2. overlay() function replaces the input with replacing , which  starts at pos and is of the length len

#Overlay
from pyspark.sql.functions import overlay
df = spark.createDataFrame([("ABCDE_XYZ", "FGH")], ("col1", "col2"))
df.select(overlay("col1", "col2", 7).alias("overlayed")).show()

#+---------+
#|overlayed|
#+---------+
#|ABCDE_FGH|
#+---------+


#encoding=utf-8

import cv2
import numpy as np
import os
import time
from matplotlib import pyplot as plt

#6种匹配算法
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
           'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']

# print(eval('cv2.TM_CCOEFF_NORMED'))#5
# print(cv2.TM_CCOEFF_NORMED)#5

#将上一个max位置矩阵内的值都置为最小值，然后重新找最大值及其位置
def getNextMax(res,max_loc,min_val,h, w):
    for i in range(max_loc[0],min(max_loc[0]+w,res.shape[1])):
        for j in range(max_loc[1],min(max_loc[1]+h,res.shape[0])):
            res[j][i]=min_val
            # print('change value')
    res = np.float32(res)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    # print('max_val:',max_val)
    return min_val, max_val, min_loc, max_loc,res
    pass

def MultiObjMatch2(image,template):
    print(type(image), image.shape)
    print(type(template), template.shape)
    # image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    # template = cv2.cvtColor(template,cv2.COLOR_BGR2GRAY)
    res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
    h, w = template.shape[0], template.shape[1]
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    print('start while')
    while max_val>0.6:
        top_left = max_loc
        bottom_right = (top_left[0] + w, top_left[1] + h)
        cv2.rectangle(image, top_left, bottom_right, 255, 2)
        min_val, max_val, min_loc, max_loc ,res= getNextMax(res,max_loc,min_val,h, w)
        print(max_val)

    cv2.imshow('res', res)
    cv2.imshow('compare', image)
    # cv2.imwrite('./images/fangmaskreslut.jpg',image)
    # cv2.imwrite('./images/fangmaskreslut_res.jpg',res)
    cv2.waitKey(0)
    if 0xFF == ord('q'):
        cv2.destroyAllWindows()
    pass

def MultiObjMatch1(image,template):
    print(type(image), image.shape)
    print(type(template), template.shape)
    # image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    # template = cv2.cvtColor(template,cv2.COLOR_BGR2GRAY)
    res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
    h, w = template.shape[0], template.shape[1]
    # res = cv2.resize(res,(image.shape[1],image.shape[0]))
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    print('maxval:',max_val,'max_loc_val:',res[max_loc[1]][max_loc[0]])
    # print(len(res),len(res[0]))
    print('type of res value:',type(res[0][0]), res.shape)
    print('type of min_val:',type(min_val))
    print('type of min_val convert to numpy:',type(np.float32(min_val)))
    # print('min:', min_val, 'max:', max_val)

    top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)
    cv2.rectangle(image, top_left, bottom_right, 255, 2)

    res = (res - min_val) / (max_val - min_val)
    for i in range(len(res)):
        for j in range(len(res[i])):
            if res[i][j] >0.95:
                print(i,j,res[i][j])
                top_left = (j,i)
                bottom_right = (top_left[0] + w, top_left[1] + h)
                cv2.rectangle(image, top_left, bottom_right, 255, 2)

    cv2.imshow('res', res)
    cv2.imshow('compare', image)
    res = res*255
    # cv2.imwrite('./images/fangmaskreslut_'+str(int(time.time()))+'.jpg',image)
    # cv2.imwrite('./images/fangmaskreslut_res_'+str(int(time.time()))+'.jpg',res)
    cv2.waitKey(0)
    if 0xFF == ord('q'):
        cv2.destroyAllWindows()
    pass

def MatchOne(image,template):
    # w, h = template.shape[::-1]
    h ,w = template.shape[0],template.shape[1]
    res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
    print(type(res), res.shape)
    # print(res)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    print(type(max_loc), type(max_val))
    top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)

    cv2.rectangle(image, top_left, bottom_right, 255, 2)

    cv2.imshow('res', res)
    cv2.imshow('result', image)
    cv2.waitKey(0)
    if 0xFF == ord('q'):
        cv2.destroyAllWindows()

if __name__ == '__main__':
    # image = cv2.imread('./mask/fang-864x576-mask.jpg',0)
    image = cv2.imread('./images/fangtianxia.jpg',0)
    # image = cv2.imread('./images/anjuke.jpg',0)
    # template = cv2.imread('./mask/fang_logo.jpg',0)
    template = cv2.imread('./mask/fixed-fang-logo.jpg',0)
    # template = cv2.imread('./mask/ajk-logo2.jpg',0)
    # print(template.shape,image.shape)
    MultiObjMatch1(image,template)
    # MatchOne(image,template)

# plt.subplot(221), plt.imshow(image, cmap="gray")
# plt.title('Original Image'), plt.xticks([]), plt.yticks([])
# plt.subplot(222), plt.imshow(template, cmap="gray")
# plt.title('template Image'), plt.xticks([]), plt.yticks([])
# plt.subplot(223), plt.imshow(res, cmap="gray")
# plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
# plt.subplot(224), plt.imshow(image, cmap="gray")
# plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
# plt.show()


"""
15. 三数之和

给你一个整数数组 nums ，判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ，
同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意：答案中不可以包含重复的三元组。
"""

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        ans = []
        target = 0

        # 先对nums排序
        nums.sort()

        for i in range(len(nums)):
            if nums[i] > target:
                return ans

            # 去重i
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            low = i + 1
            high = len(nums) - 1
            while low < high:
                if nums[i] + nums[low] + nums[high] > target:
                    high -= 1
                elif nums[i] + nums[low] + nums[high] < target:
                    low += 1
                else:
                    ans.append([nums[i], nums[low], nums[high]])
                    low += 1
                    high -= 1

                    # 去重j,k
                    while low < high and nums[low] == nums[low - 1]:
                        low += 1
                    while low < high and nums[high] == nums[high + 1]:
                        high -= 1
        return ans

if __name__ == '__main__':
    nums = [-1, 0, 1, 2, -1, -4]
    solu = Solution()
    print(solu.threeSum(nums))  # [[-1, -1, 2], [-1, 0, 1]]


from django.conf.urls.defaults import *
from django.contrib import admin
from django.views.generic import TemplateView
from django.conf import settings

admin.autodiscover()

urlpatterns = patterns('',
    (r'^i18n/', include('django.conf.urls.i18n')),
    url(r'^', include('newproject_app.appurls')),
)

if 'rosetta' in settings.INSTALLED_APPS:
  urlpatterns += patterns('',
    url(r'^admin/translation/', include('rosetta.urls')),
  )

if 'pages' in settings.INSTALLED_APPS:
  urlpatterns += patterns('',
    (r'^', include('pages.urls'))
  )

if 'profiles' in settings.INSTALLED_APPS:
  urlpatterns += patterns('profiles.views',
    url(r'^user/(?P<user_id>\d)/', 'profile', name='user-id'),
  )

if 'photologue' in settings.INSTALLED_APPS:
  urlpatterns += patterns('',
    (r'^photologue/', include('photologue.urls')),
  )

if 'imperavi' in settings.INSTALLED_APPS:
  urlpatterns += patterns('',
    (r'^imperavi/', include('imperavi.urls')),
  )

if 'video' in settings.INSTALLED_APPS:
  urlpatterns += patterns('',
    (r'^video/', include('video.urls')),
  )

urlpatterns += patterns('',
  (r'^admin/', include(admin.site.urls)),
  url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
)
if settings.DEBUG:
  urlpatterns = patterns('',
    url(r'^media/(?P<path>.*)$', 'django.views.static.serve',
        {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
    url(r'', include('django.contrib.staticfiles.urls')),
  ) + urlpatterns


from django.shortcuts import render
from django.http import Http404, HttpResponseRedirect
from .models import Article, Comment
from django.urls import reverse
from django.shortcuts import get_object_or_404

def inblog(request):
	latest_articles_list = Article.objects.order_by('-pub_date')[:10]
	return render(request, 'blog/blog.html', {'latest_articles_list': latest_articles_list})

def detail(request, article_id):
	try:
		a = Article.objects.get(id=article_id)
	except:
		raise Http404('Статья не найдена!')
	latest_comments_list = a.comment_set.order_by('-id')[:10]
	return render(request, 'blog/blog_detail.html', {'article': a, 'latest_comments_list': latest_comments_list})

def leave_comment(request, article_id):
	try:
		a = Article.objects.get(id=article_id)
		latest_comments_list = a.comment_set.order_by('-id')[:10]
	except:
		raise Http404('Статья не найдена!')
	ctx = {
		'article': a,
		'latest_comments_list': latest_comments_list
	}
	author = request.POST['name']
	comment = request.POST['text']

	if not author:
		ctx['message'] = 'Author name cannot be empty!'
		return render(request, 'blog/blog_detail.html', ctx)
	if not comment:
		ctx['message'] = 'Write comment!'
		return render(request, 'blog/blog_detail.html', ctx)

	a.comment_set.create(author_name=request.POST['name'], comment_text=request.POST['text'])
	return HttpResponseRedirect(reverse('blog:detail', args=(a.id,)))

def add_like(request, article_id):
	try:
		a = Article.objects.get(id=article_id)
		article = get_object_or_404(Article, id=article_id)
		article.likes += 1
		article.save()
	except:
		return Http404('Ошибка')
	return HttpResponseRedirect(reverse('blog:detail', args=(a.id,)))

def add_dislike(request, article_id):
	try:
		a = Article.objects.get(id=article_id)
		article = get_object_or_404(Article, id=article_id)
		article.dislikes += 1
		article.save()
	except:
		return Http404('Ошибка')
	return HttpResponseRedirect(reverse('blog:detail', args=(a.id,)))


# https://github.com/DequanWang/tent/blob/master/tent.py

from copy import deepcopy

import torch
import torch.nn as nn
import torch.jit

import math

import torchvision.transforms as transforms

augment = nn.Sequential(
    transforms.RandomHorizontalFlip(),
    transforms.RandomCrop(224, padding=(23,23)),
)
augment = torch.jit.script(augment)

from utils.polyloss import Poly1CrossEntropyLoss

# inspired by https://github.com/bethgelab/robustness/tree/aa0a6798fe3973bae5f47561721b59b39f126ab7
def find_bns(parent, prior):
    replace_mods = []
    if parent is None:
        return []
    for name, child in parent.named_children():
        child.requires_grad_(False)
        if isinstance(child, nn.BatchNorm2d):
            module = TBR(child, prior).cuda()
            replace_mods.append((parent, name, module))
        else:
            replace_mods.extend(find_bns(child, prior))
    return replace_mods

class TBR(nn.Module):
    def __init__(self, layer, prior):
        assert prior >= 0 and prior <= 1
        super().__init__()
        self.layer = layer
        self.layer.eval()
        self.prior = prior
        self.rmax = 3.0
        self.dmax = 5.0
        self.tracked_num = 0
        self.running_mean = None
        self.running_std = None

    def forward(self, input):
        batch_mean = input.mean([0, 2, 3])
        batch_std = torch.sqrt(input.var([0, 2, 3], unbiased=False) + self.layer.eps)

        if self.running_mean is None:
            self.running_mean = batch_mean.detach().clone()
            self.running_std = batch_std.detach().clone()

        r = (batch_std.detach() / self.running_std) #.clamp_(1./self.rmax, self.rmax)
        d = ((batch_mean.detach() - self.running_mean) / self.running_std) #.clamp_(-self.dmax, self.dmax)

        input = (input - batch_mean[None,:,None,None]) / batch_std[None,:,None,None] * r[None,:,None,None] + d[None,:,None,None]

        self.running_mean = self.prior * self.running_mean + (1. - self.prior) * batch_mean.detach()
        self.running_std = self.prior * self.running_std + (1. - self.prior) * batch_std.detach()

        self.tracked_num+=1

        return input * self.layer.weight[None,:,None,None] + self.layer.bias[None,:,None,None]

class MOBA(nn.Module):
    """Tent adapts a model by entropy minimization during testing.
    Once tented, a model adapts itself by updating on every forward.
    """
    def __init__(self, model, optimizer, moob_factor=0.9, buffer_size=16, temperature=10.0, class_rebalancing=False, loss_type='entropy', steps=1, episodic=False, class_num=1000):
        super().__init__()
        self.model = model
        self.optimizer = optimizer
        self.steps = steps
        assert steps > 0, "tent requires >= 1 step(s) to forward and update"
        self.episodic = episodic

        # note: if the model is never reset, like for continual adaptation,
        # then skipping the state copy would save memory
        # self.model_state, self.optimizer_state = \
        #     copy_model_and_optimizer(self.model, self.optimizer)

        self.time_decay = moob_factor
        self.class_num = class_num
        self.weight_per_class = torch.zeros(self.class_num, device="cuda") + 1 / self.class_num
        # self.n_samples_seen_per_class = torch.zeros(self.class_num, device="cuda")
        # self.n_samples_repeated_per_class = torch.zeros(self.class_num, device="cuda")
        # self.running_class_entropy_seen = []
        # self.running_class_entropy_repeated = []
        self.temperature = temperature / 10
        self.class_rebalancing = class_rebalancing
        self.loss_type = loss_type
        if self.loss_type == 'poly_loss':
            self.polyloss = Poly1CrossEntropyLoss(num_classes=self.class_num,
                        reduction='none')
        self.buffer_size = buffer_size
        self.buffer = torch.zeros(self.buffer_size, device='cuda')

    def forward(self, x, rendition_mask=None):
        if self.episodic:
            self.reset()

        for _ in range(self.steps):
            outputs = forward_and_adapt(self, x, self.model, self.optimizer, rendition_mask)

        return outputs

    def forward_only(self, x, rendition_mask=None):
        if rendition_mask is None:
            outputs = self.model(x) / self.temperature
        else:
            outputs = self.model(x)[:, rendition_mask] / self.temperature

        return outputs

    def reset(self):
        if self.model_state is None or self.optimizer_state is None:
            raise Exception("cannot reset without saved model/optimizer state")
        load_model_and_optimizer(self.model, self.optimizer,
                                 self.model_state, self.optimizer_state)

@torch.jit.script
def softmax_entropy(x: torch.Tensor) -> torch.Tensor:
    """Entropy of softmax distribution from logits."""
    return -(x.softmax(1) * x.log_softmax(1)).sum(1)

@torch.enable_grad()  # ensure grads in possible no grad context for testing
def forward_and_adapt(self, x, model, optimizer, rendition_mask=None):
    """Forward and adapt model on batch of data.
    Measure entropy of the model prediction, take gradients, and update params.
    """
    # forward
    if rendition_mask is None:
        outputs = model(x) / self.temperature
    else:
        outputs = model(x)[:, rendition_mask] / self.temperature
    pseudo_labels = outputs.argmax(1).detach()

    # adapt
    if self.loss_type == 'cross_entropy':
        cost = nn.functional.cross_entropy(outputs, pseudo_labels, reduction='none')
    elif self.loss_type == 'poly_loss':
        cost = self.polyloss(outputs, pseudo_labels)
    else:
        cost = softmax_entropy(outputs)

    # Compute number of times to take each selected sample into account in the loss
    # MOOB Wang et al. 2016
    if self.class_rebalancing:
        w_max = self.weight_per_class.max()
        # if len(pseudo_labels) > 1:
        nb_repeat = torch.poisson(w_max/self.weight_per_class[pseudo_labels])
        nb_repeat[nb_repeat == 0] = 1
        sample_weights = nb_repeat / (nb_repeat.sum() + self.buffer[0:self.buffer_size-len(nb_repeat)].sum()) * len(nb_repeat)
        self.buffer = torch.cat([nb_repeat, self.buffer[0:self.buffer_size-len(nb_repeat)]])
        # else:
            # sample_weights = -85.17 * self.weight_per_class[pseudo_labels] + 3.40
            # sample_weights = -torch.log((1 - self.time_decay) * self.class_num * self.weight_per_class[pseudo_labels])
            # sample_weights = 1
            # sample_weights = torch.tanh((w_max/self.weight_per_class[pseudo_labels]) - 1)
    else:
        sample_weights = torch.ones_like(pseudo_labels)

    loss = (cost * sample_weights).mean(0)
    loss.backward()

    optimizer.step()
    optimizer.zero_grad()

    self.weight_per_class = self.time_decay * self.weight_per_class + (1 - self.time_decay) * torch.bincount(pseudo_labels, minlength=self.class_num)

    return outputs

def collect_params(model):
    """Collect the affine scale + shift parameters from norm layers.
    Walk the model's modules and collect all normalization parameters.
    Return the parameters and their names.
    Note: other choices of parameterization are possible!
    """
    params = []
    names = []
    for nm, m in model.named_modules():
        if isinstance(m, (nn.BatchNorm2d, nn.GroupNorm, nn.LayerNorm)):
            for np, p in m.named_parameters():
                if np in ['weight', 'bias']:  # weight is scale, bias is shift
                    params.append(p)
                    names.append(f"{nm}.{np}")
    return params, names

def copy_model_and_optimizer(model, optimizer):
    """Copy the model and optimizer states for resetting after adaptation."""
    model_state = deepcopy(model.state_dict())
    optimizer_state = deepcopy(optimizer.state_dict())
    return model_state, optimizer_state

def load_model_and_optimizer(model, optimizer, model_state, optimizer_state):
    """Restore the model and optimizer states from copies."""
    model.load_state_dict(model_state, strict=True)
    optimizer.load_state_dict(optimizer_state)

def configure_model(model):
    # Replace BatchNorm2D layers by BatchRenorm2D
    replace_mods = find_bns(model, 0.95)
    for (parent, name, child) in replace_mods:
        setattr(parent, name, child)
    """Configure model for use with tent."""
    # train mode, because tent optimizes the model to minimize entropy
    model.train()
    # disable grad, to (re-)enable only what tent updates
    model.requires_grad_(False)
    # configure norm for tent updates: enable grad + force batch statisics
    for m in model.modules():
        if isinstance(m, nn.BatchNorm2d):
            m.requires_grad_(True)
            # force use of batch stats in train and eval modes
            m.track_running_stats = False
            m.running_mean = None
            m.running_var = None
        if isinstance(m, (nn.GroupNorm, nn.LayerNorm)):
            m.requires_grad_(True)
    return model

def check_model(model):
    """Check model for compatability with tent."""
    is_training = model.training
    assert is_training, "tent needs train mode: call model.train()"
    param_grads = [p.requires_grad for p in model.parameters()]
    has_any_params = any(param_grads)
    has_all_params = all(param_grads)
    assert has_any_params, "tent needs params to update: " \
                           "check which require grad"
    assert not has_all_params, "tent should not update all params: " \
                               "check which require grad"
    has_bn = any([isinstance(m, nn.BatchNorm2d) for m in model.modules()])
    assert has_bn, "tent needs normalization for its optimization"


from pathlib import Path

from .MAP_PATH import ATTR_MAP, dmgMap

R_PATH = Path(__file__).parents[1]
TEXT_PATH = R_PATH / 'texture2D'

SCORE_MAP = {
    '暴击率': 2,
    '暴击伤害': 1,
    '元素精通': 0.25,
    '元素充能效率': 0.65,
    '百分比血量': 0.86,
    '百分比攻击力': 1,
    '百分比防御力': 0.7,
    '血量': 0.014,
    '攻击力': 0.12,
    '防御力': 0.18,
}

VALUE_MAP = {
    '攻击力': 4.975,
    '血量': 4.975,
    '防御力': 6.2,
    '元素精通': 19.75,
    '元素充能效率': 5.5,
    '暴击率': 3.3,
    '暴击伤害': 6.6,
}

def strLenth(r: str, size: int, limit: int = 540) -> str:
    result = ''
    temp = 0
    for i in r:
        if temp >= limit:
            result += '\n' + i
            temp = 0
        else:
            result += i

        if i.isdigit():
            temp += round(size / 10 * 6)
        elif i == '/':
            temp += round(size / 10 * 2.2)
        elif i == '.':
            temp += round(size / 10 * 3)
        elif i == '%':
            temp += round(size / 10 * 9.4)
        else:
            temp += size
    return result

async def get_artifacts_score(subName: str, subValue: int) -> int:
    score = subValue * SCORE_MAP[subName]
    return score

async def get_artifacts_value(
    subName: str,
    subValue: float,
    baseAtk: int,
    baseHp: int,
    baseDef: int,
    charName: str,
) -> float:
    if charName not in ATTR_MAP:
        ATTR_MAP[charName] = ['攻击力', '暴击率', '暴击伤害']
    if subName in ATTR_MAP[charName] and subName in ['血量', '防御力', '攻击力']:
        if subName == '血量':
            base = (subValue / baseHp) * 100
        elif subName == '防御力':
            base = (subValue / baseDef) * 100
        elif subName == '攻击力':
            base = (subValue / baseAtk) * 100
        else:
            base = 1.0
        value = float('{:.2f}'.format(base / VALUE_MAP[subName]))
    elif subName in ['百分比血量', '百分比防御力', '百分比攻击力']:
        subName = subName.replace('百分比', '')
        if subName in ATTR_MAP[charName]:
            value = float('{:.2f}'.format(subValue / VALUE_MAP[subName]))
        else:
            return 0
    else:
        if subName in ATTR_MAP[charName]:
            value = float('{:.2f}'.format(subValue / VALUE_MAP[subName]))
        else:
            value = 0

    if charName == '胡桃' and subName == '攻击力':
        value = value * 0.4
    return value

async def get_all_artifacts_value(
    raw_data: dict, baseHp: int, baseAtk: int, baseDef: int, char_name: str
) -> float:
    artifactsValue = 0
    raw_data = raw_data['equipList']
    for aritifact in raw_data:
        for i in aritifact['reliquarySubstats']:
            subName = i['statName']
            subValue = i['statValue']
            value_temp = await get_artifacts_value(
                subName, subValue, baseAtk, baseHp, baseDef, char_name
            )
            artifactsValue += value_temp
    return artifactsValue

async def get_first_main(mainName: str) -> str:
    if '伤害加成' in mainName:
        equipMain = mainName[0]
    elif '元素' in mainName:
        equipMain = mainName[2]
    elif '百分比' in mainName:
        if '血量' in mainName:
            equipMain = '生'
        else:
            equipMain = mainName[3]
    else:
        equipMain = mainName[0]
    return equipMain

async def get_char_std(raw_data: dict, char_name: str):
    weaponName = raw_data['weaponInfo']['weaponName']

    equipMain = ''
    for aritifact in raw_data['equipList']:
        mainName = aritifact['reliquaryMainstat']['statName']
        artifactsPos = aritifact['aritifactPieceName']
        if artifactsPos == '时之沙':
            equipMain += await get_first_main(mainName)
        elif artifactsPos == '空之杯':
            equipMain += await get_first_main(mainName)
        elif artifactsPos == '理之冠':
            equipMain += await get_first_main(mainName)

    if 'equipSets' in raw_data:
        equipSets = raw_data['equipSets']
    else:
        artifact_set_list = []
        for i in raw_data['equipList']:
            artifact_set_list.append(i['aritifactSetsName'])
        equipSetList = set(artifact_set_list)
        equipSets = {'type': '', 'set': ''}
        for equip in equipSetList:
            if artifact_set_list.count(equip) >= 4:
                equipSets['type'] = '4'
                equipSets['set'] = equip
                break
            elif artifact_set_list.count(equip) == 1:
                pass
            elif artifact_set_list.count(equip) >= 2:
                equipSets['type'] += '2'
                equipSets['set'] += equip

    if equipSets['type'] in ['2', '']:
        seq = ''
    else:
        seq = '{}|{}|{}'.format(
            weaponName.replace('「', '').replace('」', ''),
            equipSets['set'],
            equipMain,
        )

    std_prop = dmgMap[char_name]
    seq_temp_a = ''
    seq_temp_w = ''
    for std_seq in std_prop:
        # 如果序列完全相同, 则直接使用这个序列
        if std_seq['seq'] == seq:
            std = std_seq
            break
        # 如果不完全相同, 但是杯子的主词条相同, 也可以使用这个
        if len(seq) >= 2 and len(std_seq['seq']) >= 2:
            if std_seq['seq'][:2] == seq[:2] and seq_temp_w == '':
                seq_temp_w = std_seq
            if std_seq['seq'][-2] == seq[-2] and seq_temp_a == '':
                seq_temp_a = std_seq
    else:
        # 如果存在备选那就用备选
        if seq_temp_w:
            std = seq_temp_w
        elif seq_temp_a:
            std = seq_temp_a
        # 不存在则使用第一个
        else:
            std = dmgMap[char_name][0]

    return std


import cv2

def detect_upper_lower(obs):
    ob1,ob2=get_two_max(obs)
    if ob1 is None:
        return None,None
    if ob1 is not None and ob2 is not None:
        x1,y1,w1,h1=cv2.boundingRect(ob1.frame)
        x2,y2,w2,h2=cv2.boundingRect(ob2.frame)
        if y1<y2:
            return ob1,ob2
        else:
            return ob2,ob1
    else:
        return ob1,None
def get_two_max(obs):
    mx,smx=None,None
    for ob in obs:
        if ob.frame is not None:
            if mx is None or cv2.contourArea(ob.frame)>cv2.contourArea(mx.frame):
                smx=mx
                mx=ob
            elif smx is None or cv2.contourArea(ob.frame)>cv2.contourArea(smx.frame):
                smx=ob
    return mx,smx


import time
import threading

def get_detail_html():
    print("start get detail html ")
    time.sleep(2)
    print("get detail html completed. ")

def get_detail_url():
    print("start get detail url ")
    time.sleep(2)
    print("get detail url completed. ")

if __name__ == '__main__':
    t1 = threading.Thread(target=get_detail_html, args="")
    t2 = threading.Thread(target=get_detail_url, args="")
    print("start time {}".format(time.time()))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    t1.setDaemon()
    t2.setDaemon()
    print("end_time {}".format(time.time()))


import time
import random

import html
import json
import requests
import time
from time import sleep
import sys
import os
from tqdm import tqdm
from sty import fg, bg, ef
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import welcome
import errors

def getanswer():
    print("freebirdFormviewerComponentsQuestionBaseTitle")

    # Chrome Driver
    data = {}
    checkPath = True
    with open('dependencies.json', 'r') as read_dependencies:
        read_dependencies.seek(0)
        checkPath = read_dependencies.readline(1)
    if not checkPath:
        chromedriver = input(fg(79, 176, 140) + "[~] Please Enter Your Driver Path : ")
        try:
            chromedriverValidated = chromedriver.endswith("chromedriver")
            data['driverPath'] = []
            data['driverPath'].append({
                    'path': chromedriver,
                    'isValidated': True
                })
            with open('dependencies.json', 'w') as write_dependencies:
                    json.dump(data, write_dependencies)
        except:
            if not chromedriverValidated:
                err = errors.geterrors(1)
                exit()
    else:
        with open('dependencies.json', 'r') as read_dependencies_path:
            mainData = json.load(read_dependencies_path)
            pathData = mainData['driverPath'][0]['path']
            chromedriver =  pathData

    chromeOptions = webdriver.ChromeOptions()
    chromeOptions.add_argument('--headless')
    chromeOptions.add_argument('--no-sandbox')
    chromeOptions.add_argument('--disable-dev-shm-usage')
    driver = webdriver.Chrome(executable_path=chromedriver, chrome_options=chromeOptions)

    # Target Form
    userlink = input(fg(79, 176, 140) + "[~] Enter Your Google Form Url: ")
    verifiedlink = userlink.startswith("https://docs.google.com/forms/") or userlink.startswith("https://forms.gle")

    # Get Link
    if driver.get(userlink):
        for i in tqdm(range(5)):
            sleep(0.2)

    # Link Verification
    if not verifiedlink:
        err = errors.geterrors(2)
        exit()

    # Target Options
    req = requests.get(userlink)
    soup = BeautifulSoup(req.content, "html.parser")

    print(fg(0, 255, 145) + "\n[!] Looking For Questions ... \n")

    questioncount = 0
    answercount = 0
    allquestions = ["sample"]
    allanswers = ["sample"]

    for questions in soup.find_all("div", class_="freebirdFormviewerComponentsQuestionBaseTitle"):
        questioncount = questioncount + 1
        convertquestioncount = str(questioncount)
        allquestions.append(questions.text)

    for answers in soup.find_all("span", class_="freebirdFormviewerComponentsQuestionRadioLabel") or soup.find_all("span", class_="freebirdFormviewerComponentsQuestionCheckboxLabel"):
        answercount = answercount + 1
        convertanswercount = str(answercount)
        allanswers.append(answers.text)

    print("Found {0} Questions !".format(convertquestioncount))
    print("Found {0} Answers !".format(convertanswercount))

    # Search Answers

    if questioncount or answercount > 0 :
        print("Looking For Answers ...")

        checkboxpath = None
        answernum = len(allanswers)
        if soup.find_all("span", class_="freebirdFormviewerComponentsQuestionCheckboxLabel"):
            choices = driver.find_elements_by_class_name("freebirdFormviewerComponentsQuestionCheckboxLabel")
            for i in range(0, len(choices)):
                if choices[i].is_displayed():
                    choices[i].click()

        radiolabelspath = None
        if soup.find_all("span", class_="freebirdFormviewerComponentsQuestionRadioLabel"):
            choices = driver.find_elements_by_class_name("freebirdFormviewerComponentsQuestionRadioLabel")
            for i in range(0, len(choices)):
                if choices[i].is_displayed():
                    choices[i].click()

        # Submit Automation
        submitpath = '//*[@id="mG61Hd"]/div[2]/div/div[3]/div/div/div/span'
        submit = driver.find_element_by_xpath(submitpath)
        webdriver.ActionChains(driver).move_to_element(submit).click(submit).perform()
        print("Submited Form !!!")

        # Click ViewScore
        sleep(3.5)
        viewscorepath = "/html/body/div[1]/div[2]/div[1]/div/div[4]/div/a/span/span"
        viewscore = driver.find_element_by_xpath(viewscorepath)
        webdriver.ActionChains(driver).move_to_element(viewscore).click(viewscore).perform()
        print("Clicked ViewScore !!!")

        # Find Answers
        sleep(1)
        driver.get("https://docs.google.com/forms/d/e/1FAIpQLSdiwAWqBjTPQfAYiiK98Ttex6QRjJ7lAhA7CGU4wTlItmBShw/viewscore?viewscore=AE0zAgAtBzriNp-WZEzlULNAhMQnSOioffHO0OfkO7r3Yt10T4jFK6-rTXJ4rpgXebUKplQ")
        sleep(3)

        finddivs = driver.find_elements_by_class_name("freebirdFormviewerViewItemsCheckboxLabel")
        findanswers = driver.find_elements_by_class_name("freebirdFormviewerViewItemsCheckboxLabel")

        foundanswer = None
        if findanswers:
            for i in range(1, len(finddivs)):
                foundanswer = '/html/body/div/div[2]/div[1]/div/div[2]/div[{0}]/div/div[4]/div[2]/div/label/div/div[2]/div/span'.format(str(i))
                target = driver.find_element_by_xpath(foundanswer)
                if target.is_displayed():
                    print(target.text)
                else:
                    print("Already Correct !")

    else:
        print(fg(255, 76, 36) + "No Questions or Answers Found !")


alumnos1 = [1,2,3,4,5,3,1,3,5,6,3,1]

alumnos2 = [8,7,8,9,7,9,8,6,9,10]

alumnos3 = [6,7,8,9,10]

suma = 0
for alumno in alumnos1:
    suma += alumno

resultado = suma/len(alumnos1)
print(resultado)

suma = 0
for alumno in alumnos2:
    suma += alumno

resultado = suma/len(alumnos1)
print(resultado)

suma = 0
for alumno in alumnos3:
    suma += alumno

resultado = suma/len(alumnos1)
print(resultado)

#camelCase
#def calcularPromedio():

#1. Definir la funcion
#snake_case
def calcular_promedio(lista):
    print(lista)
    print("Ejecución de la función")
    suma = 0
    for numero in lista:
        suma += numero

    resultado = suma/len(lista)
    print(resultado)

print("Esto no es parte de la funcion")

#2. Ejecutando la funcion
calcular_promedio(alumnos1)
calcular_promedio(alumnos2)
calcular_promedio(alumnos3)

# Utilizando return
def sumar(p1,p2):
    print(resultado)
    resultado_suma = p1 + p2
    resultado_suma2 = p1 + p2 + 5
    print(resultado_suma)

    return resultado_suma,resultado_suma2

res_sum = sumar(10,5) # Toda la expresión tiene el valor de: resultado_suma

print(res_sum)

# calculo_impuesto = res_sum * 0.16

# print(calculo_impuesto)


#!/usr/bin/python3
""" objects that handle all default RestFul API  for answers"""
from api.v1.views import app_views
from flask import abort, jsonify, make_response, render_template, request
from models.question import Question
import models
from models.survey_section import Survey_Section
from models.answer import Answer
from models.evaluation import Evaluation
from models.risk_profile import Risk_Profile
from models.user import User

# session = models.session

# @app_views.route('/risk_profile', methods=['POST'], strict_slashes=False)
# def save_answers():
#     """
#     Return all answers
#     """
#     data = session.query(User).join(Evaluation, Evaluacion.id_user==User.id).join(Risk_Profile, Evaluation.id_risk_profile==Risk_Profile.id).first()
#     if data is None: return abort
#     print(data)
#     # return jsonify({'status': 'ok'})
#     return data

@app_views.route('/answers', methods=['POST'], strict_slashes=False)
def save_answers():
    """
    Return all answers
    """
    data = request.get_json()
    answer_list = data.get('answers')
    result = data.get('res')
    id_survey = data.get('id_survey')
    id_user = data.get('id_user')
    id_risk_profile = data.get('id_risk_profile')
    id_age = data.get('user_age')
    for answer in answer_list:
        ans = Answer(
        id_user=answer.get('user_id'),
        id_question_option=answer.get('id_question_option'),
        answer_value=answer.get('answer_value'),
        active=answer.get('active'),
        id_question=answer.get('id_question'),
        id_survey=answer.get('id_survey'),
        id_survey_section=answer.get('id_survey_section'),
        create_by=answer.get('user_id')
        )
        models.session.add(ans)
    models.session.commit()

    # Comparamos el id_risk_profile con el resultado obtenido
    if result is None:
        id_risk_profile = None;
        msg = 'Usted por el momento no puede acceder a nuestros productos.';
    elif result < -1:
        id_risk_profile = 1
        msg = 'Usted tiene un perfil tipo: Adverso

Un asesor se comunicará con usted próximamente.'
    elif result <= 0:
        id_risk_profile = 2
        msg = 'Usted tiene un perfil tipo: Moderado Adverso

Un asesor se comunicará con usted próximamente.'
    elif result > 0.75:
        id_risk_profile = 4
        msg = 'Usted tiene un perfil tipo: Agresivo

Un asesor se comunicará con usted próximamente.'
    else:
        id_risk_profile = 3;
        msg = 'Usted tiene un perfil tipo: Moderado Agresivo

Un asesor se comunicará con usted próximamente.'

    evaluation = Evaluation(
        id_risk_profile=id_risk_profile,
        id_survey=id_survey,
        id_user=id_user,
        result=result
    )
    models.session.add(evaluation)
    models.session.commit()

    print(evaluation)

    user = models.session.query(User).filter(User.id==id_user).first()
    user.age = id_age
    models.session.add(user)
    models.session.commit()

    print(result)
    print(user.age)

    print(models.session.query(Evaluation).all())
    return jsonify({'status': 'ok', 'msg': msg})


"""
Suppose you're analyzing a population of 100,000 people, and you're trying to understand life expectancy. Within this
population of 100,000 people, 75% can expect to live to the age of 70, while 45% can expect to live to age 80. Given
that a person is 70, what is the probability that they live to the age 80?
"""

if __name__ == '__main__':
    population = 100_000
    live_to_70 = .75
    live_to_80 = .45

    # P(A|B) = P(B|A) . P(A) / P(B)
    # P(B|A) is probability to live to 70 given that the person is 80
    p_BA = 1
    # P(A) probability to live to 80
    p_A = live_to_80
    # P(B) probability to live to 70
    p_B = live_to_70

    p_AB = p_BA * p_A / p_B
    print(p_AB)


import pandas as pd

def sma(source: list, length: int, has_null: bool = True) -> list:
    """
    * Calculate the Simple Moving Average (SMA) of a list of data.
    *  Parameters:
    * - source (list): The source data for the calculation.
    * - length (int): The range of each calculation
    * - has_null (bool): Decide if null entries should be included or not.
    * Returns:
    * - list: The SMA of the source data.
    """

    # Convert array of integers to pandas series
    numbers_series = pd.Series(source)

    # Get the window of series
    # of observations of specified window size
    windows = numbers_series.rolling(length)

    # Create a series of moving
    # averages of each window
    moving_averages = windows.mean()

    # Convert pandas series back to list
    moving_averages_list = moving_averages.tolist()

    # Remove null entries from the list
    final_list = moving_averages_list[length - 1:]

    # If the developer wants null entries it will be returned.
    return (moving_averages_list if has_null else final_list)

def ema(source: list, length: int) -> list:
    """
    * The ema function returns the exponentially weighted moving average.
    * Parameters:
    * - source (list): The source data for the calculation.
    * - length (int): The range of each calculation.
    * Returns
    * - list: Exponential Moving Average list
    """

    # EMA Alpha
    alpha = 2 / (length + 1)

    # Sets the initial value of the ema list.
    final_list = [source[0]]

    for source_value, pointer in zip(source, range(len(source))):
        # Based on the formula, EMA = alpha * source + (1 - alpha) * EMA[1]
        final_list.append(alpha * source_value + (1 - alpha) * final_list[pointer])

    return final_list[1:]

def rma(source: list, length: int) -> list:
    """
    * It is the exponentially weighted moving average with alpha = 1 / length.
    * Parameters:
    * - source (list): The source data for calculations.
    * - length (int): The range of each calculation.
    * Returns:
    * - list: Running moving average (RMA) list.
    """
    # RMA Alpha
    alpha = 1.0 / length

    # Sets the initial value of the ema list.
    final_list = [source[0]]

    for source_value, pointer in zip(source, range(len(source))):
        # Based on the formula, RMA = alpha * source + (1 - alpha) * RMA[1]
        final_list.append(alpha * source_value + (1 - alpha) * final_list[pointer])

    return final_list


from .models import UserProfile
import string
import random

def login_signals_to_clear_current_session_key(sender, request, user, **kwargs):
    if request.method == "POST":
        try:
            keytodelte = request.session.__getitem__("next")
        except KeyError:
            print("key doesn't exist")
        else:
            request.session.__delitem__("next")

    print("keys cleared successfully ")

def unique_customer_id(id_length=10, char=string.digits+string.ascii_uppercase):
    unique_list = ((random.choice(char)) for i in range(id_length))
    unique_code = "".join(unique_list)
    return unique_code

def post_save_Profile_creation(sender, instance, created, *args, **kwargs):
    if created:
        profile = UserProfile.objects.create(
            user=instance,
            first_name=instance.first_name,
            last_name=instance.last_name,
            unique_id=unique_customer_id()
        )
        profile.save()


from collections import OrderedDict, defaultdict

from sympy import And

from devito.ir import (Forward, GuardBoundNext, Queue, Vector, WaitLock, WithLock,
                       FetchUpdate, PrefetchUpdate, ReleaseLock, normalize_syncs)
from devito.passes.clusters.utils import bind_critical_regions, is_memcpy
from devito.symbolics import IntDiv, uxreplace
from devito.tools import OrderedSet, is_integer, timed_pass
from devito.types import CustomDimension, Lock

__all__ = ['Tasker', 'Streaming']

class Asynchronous(Queue):

    def __init__(self, key, sregistry):
        assert callable(key)
        self.key = key
        self.sregistry = sregistry

        super().__init__()

class Tasker(Asynchronous):

    """
    Create asynchronous Clusters, or "tasks".

    Parameters
    ----------
    key : callable, optional
        A Cluster `c` becomes an asynchronous task only if `key(c)` returns True

    Notes
    -----
    From an implementation viewpoint, an asynchronous Cluster is a Cluster
    with attached suitable SyncOps, such as WaitLock, WithLock, etc.
    """

    @timed_pass(name='tasker')
    def process(self, clusters):
        return super().process(clusters)

    def callback(self, clusters, prefix):
        if not prefix:
            return clusters

        d = prefix[-1].dim

        locks = {}
        waits = defaultdict(OrderedSet)
        tasks = defaultdict(list)
        for c0 in clusters:
            dims = self.key(c0)
            if d not in dims:
                # Not a candidate asynchronous task
                continue

            # Prevent future writes to interfere with a task by waiting on a lock
            may_require_lock = c0.scope.reads

            # We can ignore scalars as they're passed by value
            may_require_lock = {f for f in may_require_lock if f.is_AbstractFunction}

            # Sort for deterministic code generation
            may_require_lock = sorted(may_require_lock, key=lambda i: i.name)

            protected = defaultdict(set)
            for c1 in clusters:
                offset = int(clusters.index(c1) <= clusters.index(c0))

                for target in may_require_lock:
                    try:
                        writes = c1.scope.writes[target]
                    except KeyError:
                        # No read-write dependency, ignore
                        continue

                    try:
                        if all(w.aindices[d].is_Stepping for w in writes) or \
                           all(w.aindices[d].is_Modulo for w in writes):
                            size = target.shape_allocated[d]
                            assert is_integer(size)
                            ld = CustomDimension(name='ld', symbolic_size=size, parent=d)
                        elif all(w[d] == 0 for w in writes):
                            # Special case, degenerates to scalar lock
                            raise KeyError
                        else:
                            # Functions over non-stepping Dimensions need no lock
                            continue
                    except (AttributeError, KeyError):
                        # Would degenerate to a scalar, but we rather use a lock
                        # of size 1 for simplicity
                        ld = CustomDimension(name='ld', symbolic_size=1, parent=d)

                    try:
                        lock = locks[target]
                    except KeyError:
                        name = self.sregistry.make_name(prefix='lock')
                        lock = locks[target] = Lock(name=name, dimensions=ld)

                    for w in writes:
                        try:
                            index = w[d]
                            logical_index = index + offset
                        except TypeError:
                            assert ld.symbolic_size == 1
                            index = 0
                            logical_index = 0

                        if logical_index in protected[target]:
                            continue

                        waits[c1].add(WaitLock(lock[index], target))
                        protected[target].add(logical_index)

            # Taskify `c0`
            for target in protected:
                lock = locks[target]

                indices = sorted({r[d] for r in c0.scope.reads[target]})
                if indices == [None]:
                    # `lock` is protecting a Function which isn't defined over `d`
                    # E.g., `d=time` and the protected function is `a(x, y)`
                    assert lock.size == 1
                    indices = [0]

                if wraps_memcpy(c0):
                    e = c0.exprs[0]
                    function = e.lhs.function
                    findex = e.lhs.indices[d]
                else:
                    # Only for backwards compatibility (e.g., tasking w/o buffering)
                    function = None
                    findex = None

                for i in indices:
                    tasks[c0].append(ReleaseLock(lock[i], target))
                    tasks[c0].append(WithLock(lock[i], target, i, function, findex, d))

        # CriticalRegions preempt WaitLocks, by definition
        mapper = bind_critical_regions(clusters)
        for c in clusters:
            for c1 in mapper.get(c, []):
                waits[c].update(waits.pop(c1, []))

        processed = []
        for c in clusters:
            if waits[c] or tasks[c]:
                processed.append(c.rebuild(syncs={d: list(waits[c]) + tasks[c]}))
            else:
                processed.append(c)

        return processed

class Streaming(Asynchronous):

    """
    Tag Clusters with SyncOps to stream Functions in and out the device memory.

    Parameters
    ----------
    key : callable, optional
        Return the Functions that need to be streamed in a given Cluster.
    """

    @timed_pass(name='streaming')
    def process(self, clusters):
        return super().process(clusters)

    def callback(self, clusters, prefix):
        if not prefix:
            return clusters

        it = prefix[-1]
        d = it.dim

        actions = defaultdict(Actions)

        # Case 1
        if d.is_Custom and is_integer(it.size):
            for c in clusters:
                dims = self.key(c)
                if d._defines & dims:
                    if wraps_memcpy(c):
                        # Case 1A (special case, leading to more efficient streaming)
                        self._actions_from_init(c, prefix, actions)
                    else:
                        # Case 1B (actually, we expect to never end up here)
                        raise NotImplementedError

        # Case 2
        else:
            mapper = OrderedDict([(c, wraps_memcpy(c)) for c in clusters
                                  if d in self.key(c)])

            # Case 2A (special case, leading to more efficient streaming)
            if all(mapper.values()):
                for c in mapper:
                    self._actions_from_update_memcpy(c, clusters, prefix, actions)

            # Case 2B
            elif mapper:
                # There used to be a handler for this case, but it was dropped
                # because it created inefficient code and in practice never used
                raise NotImplementedError

        # Perform the necessary actions; this will ultimately attach SyncOps to Clusters
        processed = []
        for c in clusters:
            v = actions[c]

            if v.drop:
                assert not v.syncs
                continue
            elif v.syncs:
                processed.append(c.rebuild(syncs=normalize_syncs(c.syncs, v.syncs)))
            else:
                processed.append(c)

            if v.insert:
                processed.extend(v.insert)

        return processed

    def _actions_from_init(self, cluster, prefix, actions):
        it = prefix[-1]
        d = it.dim
        try:
            pd = prefix[-2].dim
        except IndexError:
            pd = None

        e = cluster.exprs[0]
        function = e.rhs.function
        target = e.lhs.function

        findex = e.rhs.indices[d]

        size = d.symbolic_size
        assert is_integer(size)

        actions[cluster].syncs[pd].append(
            FetchUpdate(None, target, 0, function, findex, d, size)
        )

    def _actions_from_update_memcpy(self, cluster, clusters, prefix, actions):
        it = prefix[-1]
        d = it.dim
        direction = it.direction

        # Prepare the data to instantiate a PrefetchUpdate SyncOp
        e = cluster.exprs[0]
        function = e.rhs.function
        target = e.lhs.function

        fetch = e.rhs.indices[d]
        if direction is Forward:
            findex = fetch + 1
        else:
            findex = fetch - 1

        # If fetching into e.g. `ub[sb1]` we'll need to prefetch into e.g. `ub[sb0]`
        tindex0 = e.lhs.indices[d]
        if is_integer(tindex0) or isinstance(tindex0, IntDiv):
            tindex = tindex0
        else:
            assert tindex0.is_Modulo
            subiters = [i for i in cluster.sub_iterators[d] if i.parent is tindex0.parent]
            osubiters = sorted(subiters, key=lambda i: Vector(i.offset))
            n = osubiters.index(tindex0)
            if direction is Forward:
                tindex = osubiters[(n + 1) % len(osubiters)]
            else:
                tindex = osubiters[(n - 1) % len(osubiters)]

        # We need a lock to synchronize the copy-in
        name = self.sregistry.make_name(prefix='lock')
        ld = CustomDimension(name='ld', symbolic_size=1, parent=d)
        lock = Lock(name=name, dimensions=ld)
        handle = lock[0]

        # Turn `cluster` into a prefetch Cluster
        expr = uxreplace(e, {tindex0: tindex, fetch: findex})

        guards = {d: And(
            cluster.guards.get(d, True),
            GuardBoundNext(function.indices[d], direction),
        )}

        syncs = {d: [ReleaseLock(handle, target),
                     PrefetchUpdate(handle, target, tindex, function, findex, d, 1,
                                    e.rhs)]}

        pcluster = cluster.rebuild(exprs=expr, guards=guards, syncs=syncs)

        # Since we're turning `e` into a prefetch, we need to:
        # 1) attach a WaitLock SyncOp to the first Cluster accessing `target`
        # 2) insert the prefetch Cluster right after the last Cluster accessing `target`
        # 3) drop the original Cluster performing a memcpy-like fetch
        n = clusters.index(cluster)
        first = None
        last = None
        for c in clusters[n+1:]:
            if target in c.scope.reads:
                if first is None:
                    first = c
                last = c
        assert first is not None
        assert last is not None
        actions[first].syncs[d].append(WaitLock(handle, target))
        actions[last].insert.append(pcluster)
        actions[cluster].drop = True

        return last, pcluster

# Utilities

class Actions(object):

    def __init__(self, drop=False, syncs=None, insert=None):
        self.drop = drop
        self.syncs = syncs or defaultdict(list)
        self.insert = insert or []

def wraps_memcpy(cluster):
    if len(cluster.exprs) != 1:
        return False

    return is_memcpy(cluster.exprs[0])


############ if ###################

# weather = input("오늘 날씨는 어때요?")
# if weather == "비" or "눈":
#     print("우산을 챙기세요")
# elif weather == "미세먼지":
#     print("마스크를 챙기세요")
# else:
#     print("준비물 필요 없어요")

# temp = int(input("기온이 어때요?"))
# if 30 <= temp:
#     print("너무 더움")
# elif 10 <= temp and temp < 30:
#     print("괜찮은 날씨")
# elif 0 <= temp and temp < 10:
#     print("약간 추움")
# else:
#     print("너무 추움")

############ if ###################

############ for ###################

# print("대기번호: 1")
# print("대기번호: 2")
# print("대기번호: 3")
# print("대기번호: 4")

# for waiting_n in range(1, 6):
#     print("대기번호: {0}".format(waiting_n))

# starbucks = ["아이언맨", "토르", "그루트"]
# for customer in starbucks:
#     print("{0}님, 커피가 준비 되었습니다".format(customer))

############ for ###################

############ while ###################

# customer = "아이언맨"
# # index = 5
# # while index >=1:
# #     print("{0}, 커피 나왔습니다. {1}번 남았어요".format(customer, index))
# #     index -= 1
# #     if index == 0 :
# #         print("커피 폐기처분")
# index = 1
# while true:
#     print("{0}, 커피 나왔습니다. 호출 {1} 회".format(customer, index))
#     index += 1

# customer = "토르"
# person = "Unknown"

# while person != customer:
#     print("{0}, 커피 준비 완료.".format(customer))
#     person = input("이름이 뭔가요?")

############ while ###################

# absent = [2, 5] # 결석
# no_book = [7] # 책 없음
# for student in range(1, 11):
#     if student in absent:
#         continue
#     elif student in no_book:
#         print("오늘 수업 여기까지. {0} 은 교무실로".format(student))
#         break
#     print("{0}, 책 읽어봐".format(student))

# 한줄 for#######################3
# student = [1, 2, 3, 4, 5]
# print(student)
# student = [i+100 for i in student]
# print(student)

# 이름 길이로 변환
student = ["Iron man", "Thor", "Groot"]
student = [len(i) for i in student]
print(student)

# 대문자로
student = ["Iron man", "Thor", "Groot"]
student = [i.upper() for i in student]
print(student)


# 5. Longest Palindromic Substring
#

def longestPalindrome(s):
  s2 = list(s)
  s2 = ' '.join(s2)
  s2 = ' ' + s2 + ' '
  longPalindrom = ''
  radius = 0
  center = 0
  for index, char in enumerate(s2):
    i = 0
    while index-i >= 0 and index+i < len(s2) and s2[index-i] == s2[index+i]:
      i += 1
    if i > radius:
      radius = i
      center = index
  longPalindrom = s2[center-radius+1:center+radius:1]
  longPalindrom = longPalindrom.replace(" ", "")
  return longPalindrom

print(longestPalindrome('rqdbdqwtqdbdqt'))
print(longestPalindrome('abbc'))
print(longestPalindrome('abb'))


# fizz_buzz algorithm

def fizz_buzz(input):
    if (input*3) % 5 == 0:
        return "FizzBuzz"
    if input % 3 == 0:
        return "Fizz"
    if input % 5 == 0:
        return "Buzz"
    else:
        return input

# no need for "else"; just put "return input"
print(fizz_buzz(555555))