import gym
import numpy as np
import tensorflow as tf
import argparse
import itertools
import time
import os
import pickle
import code
import random

from ddpg import Actor, Critic
from memory import Memory
from ornstein_uhlenbeck import OrnsteinUhlenbeckActionNoise
from make_env import make_env
import general_utilities
import simple_tag_utilities

def play(episodes, is_render, is_testing, checkpoint_interval,
         weights_filename_prefix, csv_filename_prefix, batch_size):
    # init statistics. NOTE: simple tag specific!
    statistics_header = ["episode"]
    statistics_header.append("steps")
    statistics_header.extend(["reward_{}".format(i) for i in range(env.n)])
    statistics_header.extend(["loss_{}".format(i) for i in range(env.n)])
    statistics_header.extend(["collisions_{}".format(i) for i in range(env.n)])
    statistics_header.extend(["ou_theta_{}".format(i) for i in range(env.n)])
    statistics_header.extend(["ou_mu_{}".format(i) for i in range(env.n)])
    statistics_header.extend(["ou_sigma_{}".format(i) for i in range(env.n)])
    statistics_header.extend(["ou_dt_{}".format(i) for i in range(env.n)])
    statistics_header.extend(["ou_x0_{}".format(i) for i in range(env.n)])
    print("Collecting statistics {}:".format(" ".join(statistics_header)))
    statistics = general_utilities.Time_Series_Statistics_Store(
        statistics_header)

    for episode in range(args.episodes):
        states = env.reset()
        episode_losses = np.zeros(env.n)
        episode_rewards = np.zeros(env.n)
        collision_count = np.zeros(env.n)
        steps = 0

        while True:
            steps += 1

            # render
            if args.render:
                env.render()

            # act
            actions = []
            for i in range(env.n):
                action = np.clip(
                    actors[i].choose_action(states[i]) + actors_noise[i](), -2, 2)
                actions.append(action)

            # step
            states_next, rewards, done, info = env.step(actions)

            # learn
            if not args.testing:
                size = memories[0].pointer
                batch = random.sample(range(size), size) if size < batch_size else random.sample(
                    range(size), batch_size)

                for i in range(env.n):
                    if done[i]:
                        rewards[i] -= 50

                    memories[i].remember(states[i], actions[i],
                                         rewards[i], states_next[i], done[i])

                    if memories[i].pointer > batch_size * 10:
                        s, a, r, sn, _ = memories[i].sample(batch)
                        r = np.reshape(r, (batch_size, 1))
                        loss = critics[i].learn(s, a, r, sn)
                        actors[i].learn(s)
                        episode_losses[i] += loss
                    else:
                        episode_losses[i] = -1

            states = states_next
            episode_rewards += rewards
            collision_count += np.array(
                simple_tag_utilities.count_agent_collisions(env))

            # reset states if done
            if any(done):
                episode_rewards = episode_rewards / steps
                episode_losses = episode_losses / steps

                statistic = [episode]
                statistic.append(steps)
                statistic.extend([episode_rewards[i] for i in range(env.n)])
                statistic.extend([episode_losses[i] for i in range(env.n)])
                statistic.extend(collision_count.tolist())
                statistic.extend([actors_noise[i].theta for i in range(env.n)])
                statistic.extend([actors_noise[i].mu for i in range(env.n)])
                statistic.extend([actors_noise[i].sigma for i in range(env.n)])
                statistic.extend([actors_noise[i].dt for i in range(env.n)])
                statistic.extend([actors_noise[i].x0 for i in range(env.n)])
                statistics.add_statistics(statistic)
                if episode % 25 == 0:
                    print(statistics.summarize_last())
                break

        if episode % checkpoint_interval == 0:
            statistics.dump("{}_{}.csv".format(
                csv_filename_prefix, episode))
            if not os.path.exists(weights_filename_prefix):
                os.makedirs(weights_filename_prefix)
            save_path = saver.save(session, os.path.join(
                weights_filename_prefix, "models"), global_step=episode)
            print("saving model to {}".format(save_path))
            if episode >= checkpoint_interval:
                os.remove("{}_{}.csv".format(csv_filename_prefix,
                                             episode - checkpoint_interval))

    return statistics

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--env', default='simple_tag_guided', type=str)
    parser.add_argument('--video_dir', default='videos/', type=str)
    parser.add_argument('--learning_rate', default=0.001, type=float)
    parser.add_argument('--episodes', default=100000, type=int)
    parser.add_argument('--video_interval', default=1000, type=int)
    parser.add_argument('--render', default=False, action="store_true")
    parser.add_argument('--benchmark', default=False, action="store_true")
    parser.add_argument('--experiment_prefix', default=".",
                        help="directory to store all experiment data")
    parser.add_argument('--weights_filename_prefix', default='/save/tag-ddpg',
                        help="where to store/load network weights")
    parser.add_argument('--csv_filename_prefix', default='/save/statistics-ddpg',
                        help="where to store statistics")
    parser.add_argument('--checkpoint_frequency', default=500, type=int,
                        help="how often to checkpoint")
    parser.add_argument('--testing', default=False, action="store_true",
                        help="reduces exploration substantially")
    parser.add_argument('--load_weights_from_file', default='',
                        help="where to load network weights")
    parser.add_argument('--random_seed', default=2, type=int)
    parser.add_argument('--memory_size', default=10000, type=int)
    parser.add_argument('--batch_size', default=128, type=int)
    parser.add_argument('--ou_mus', nargs='+', type=float,
                        help="OrnsteinUhlenbeckActionNoise mus for each action for each agent")
    parser.add_argument('--ou_sigma', nargs='+', type=float,
                        help="OrnsteinUhlenbeckActionNoise sigma for each agent")
    parser.add_argument('--ou_theta', nargs='+', type=float,
                        help="OrnsteinUhlenbeckActionNoise theta for each agent")
    parser.add_argument('--ou_dt', nargs='+', type=float,
                        help="OrnsteinUhlenbeckActionNoise dt for each agent")
    parser.add_argument('--ou_x0', nargs='+', type=float,
                        help="OrnsteinUhlenbeckActionNoise x0 for each agent")

    args = parser.parse_args()

    general_utilities.dump_dict_as_json(vars(args),
                                        args.experiment_prefix + "/save/run_parameters.json")

    # init env
    env = make_env(args.env, args.benchmark)

    # Extract ou initialization values
    if args.ou_mus is not None:
        if len(args.ou_mus) == sum([env.action_space[i].n for i in range(env.n)]):
            ou_mus = []
            prev_idx = 0
            for space in env.action_space:
                ou_mus.append(
                    np.array(args.ou_mus[prev_idx:prev_idx + space.n]))
                prev_idx = space.n
            print("Using ou_mus: {}".format(ou_mus))
        else:
            raise ValueError(
                "Must have enough ou_mus for all actions for all agents")
    else:
        ou_mus = [np.zeros(env.action_space[i].n) for i in range(env.n)]

    if args.ou_sigma is not None:
        if len(args.ou_sigma) == env.n:
            ou_sigma = args.ou_sigma
        else:
            raise ValueError("Must have enough ou_sigma for all agents")
    else:
        ou_sigma = [0.3 for i in range(env.n)]

    if args.ou_theta is not None:
        if len(args.ou_theta) == env.n:
            ou_theta = args.ou_theta
        else:
            raise ValueError("Must have enough ou_theta for all agents")
    else:
        ou_theta = [0.15 for i in range(env.n)]

    if args.ou_dt is not None:
        if len(args.ou_dt) == env.n:
            ou_dt = args.ou_dt
        else:
            raise ValueError("Must have enough ou_dt for all agents")
    else:
        ou_dt = [1e-2 for i in range(env.n)]

    if args.ou_x0 is not None:
        if len(args.ou_x0) == env.n:
            ou_x0 = args.ou_x0
        else:
            raise ValueError("Must have enough ou_x0 for all agents")
    else:
        ou_x0 = [None for i in range(env.n)]

    # if not os.path.exists(args.video_dir):
    #     os.makedirs(args.video_dir)
    # args.video_dir = os.path.join(
    #     args.video_dir, 'monitor-' + time.strftime("%y-%m-%d-%H-%M"))
    # if not os.path.exists(args.video_dir):
    #     os.makedirs(args.video_dir)
    # env = MyMonitor(env, args.video_dir,
    #                 # resume=True, write_upon_reset=True,
    #                 video_callable=lambda episode: (
    #                     episode + 1) % args.video_interval == 0,
    #                 force=True)

    # set random seed
    env.seed(args.random_seed)
    random.seed(args.random_seed)
    np.random.seed(args.random_seed)
    tf.set_random_seed(args.random_seed)

    # init actors and critics
    session = tf.Session()

    actors = []
    critics = []
    actors_noise = []
    memories = []
    for i in range(env.n):
        n_action = env.action_space[i].n
        state_size = env.observation_space[i].shape[0]
        state = tf.placeholder(tf.float32, shape=[None, state_size])
        reward = tf.placeholder(tf.float32, [None, 1])
        state_next = tf.placeholder(tf.float32, shape=[None, state_size])
        speed = 0.9 if env.agents[i].adversary else 1

        actors.append(Actor('actor' + str(i), session, n_action, speed,
                            state, state_next))
        critics.append(Critic('critic' + str(i), session, n_action,
                              actors[i].eval_actions, actors[i].target_actions,
                              state, state_next, reward))
        actors[i].add_gradients(critics[i].action_gradients)
        actors_noise.append(OrnsteinUhlenbeckActionNoise(
            mu=ou_mus[i],
            sigma=ou_sigma[i],
            theta=ou_theta[i],
            dt=ou_dt[i],
            x0=ou_x0[i]))
        memories.append(Memory(args.memory_size))

    session.run(tf.global_variables_initializer())
    saver = tf.train.Saver(max_to_keep=10000000)

    if args.load_weights_from_file != "":
        saver.restore(session, args.load_weights_from_file)
        print("restoring from checkpoint {}".format(
            args.load_weights_from_file))

    start_time = time.time()

    # play
    statistics = play(args.episodes, args.render, args.testing,
                      args.checkpoint_frequency,
                      args.experiment_prefix + args.weights_filename_prefix,
                      args.experiment_prefix + args.csv_filename_prefix,
                      args.batch_size)

    # bookkeeping
    print("Finished {} episodes in {} seconds".format(args.episodes,
                                                      time.time() - start_time))
    tf.summary.FileWriter(args.experiment_prefix +
                          args.weights_filename_prefix, session.graph)
    save_path = saver.save(session, os.path.join(
        args.experiment_prefix + args.weights_filename_prefix, "models"), global_step=args.episodes)
    print("saving model to {}".format(save_path))
    statistics.dump(args.experiment_prefix + args.csv_filename_prefix + ".csv")


import argparse
from subprocess import Popen

parser = argparse.ArgumentParser(description="remove a submodule")

parser.add_argument('submodule',type=str)

args = parser.parse_args()

if not args.submodule:
    exit()

print(f"removing submodule {args.submodule}")

try:
    Popen(f"git submodule deinit {args.submodule}").wait()
    Popen(f"git rm {args.submodule}").wait()
    Popen(f"git commit -am \"removed submodule {args.submodule}\"").wait()
    Popen(f"rm -rf .git/modules/{args.submodule}").wait()
    print("removal succeeded")

except:
    print("removal failed")
    Popen(f"git reset --hard")


import json
from django import test
from localground.apps.site import models
from localground.apps.site.tests import Client, ModelMixin
from rest_framework import status
from localground.apps.site.api.tests.renderer_tests import mixins

class GeoJSONRendererListTest(test.TestCase, ModelMixin):
    '''
    These tests test the GeoJSON renderer using the /api/0/markers/ (though any
    geospatial endpoint could be used).
    '''

    def setUp(self):
        ModelMixin.setUp(self)
        self.url = '/api/0/markers/.geojson'

    def test_geojson_format_looks_correct(self):
        self.create_marker(self.user, self.project, name="Marker 1", geoJSON=mixins.point)
        self.create_marker(self.user, self.project, name="Marker 2", geoJSON=mixins.line)
        response = self.client_user.get(self.url)
        data = json.loads(response.content)

        # Check outer attributes:
        self.assertEqual(data.get("type"), "FeatureCollection")
        self.assertEqual(len(data.get("features")), 2)
        rec = data.get("features")[1]

        #Check inner attributes:
        self.assertEqual(rec.get("type"), "Feature")
        self.assertEqual(rec.get("geometry"), mixins.line)
        self.assertTrue(isinstance(rec.get("properties"), dict))

        # Make sure 'extras' attribute gets merged into the properties:
        self.assertEqual(rec.get("properties").get("key"), "value")

    def tearDown(self):
        models.Form.objects.all().delete()

class GeoJSONRendererInstanceTest(test.TestCase, ModelMixin):

    def setUp(self):
        ModelMixin.setUp(self)
        self.marker = self.create_marker(self.user, self.project, name="Marker 1", geoJSON=mixins.point)
        self.url = '/api/0/markers/%s/.geojson' % self.marker.id

    def test_geojson_format_looks_correct(self):
        response = self.client_user.get(self.url, format='json')
        data = json.loads(response.content)

        self.assertEqual(data.get("type"), "Feature")
        self.assertEqual(data.get("geometry"), mixins.point)
        self.assertTrue(isinstance(data.get("properties"), dict))

        # Make sure 'extras' attribute gets merged into the properties:
        self.assertEqual(data.get("properties").get("key"), "value")


# -*- coding: utf-8 -*-
import scrapy
import json
import re
from ..items import CcItem

class CcspiSpider(scrapy.Spider):
    name = 'ccspi'
    allowed_domains = ['toutiao.com']
    start_urls = ['http://toutiao.com/']

    def parse(self, response):
        item_url = "http://www.baidu.com"
        f = open('keyword.txt',encoding='utf-8').read().split('\n')
        for key in f:
            url = "https://www.toutiao.com/search/?keyword="+key
            yield scrapy.Request(url,callback=self.Read_key,meta={'key':key})

    def Read_key(self,response):
        key = response.meta['key']
        for i in range(0,8):
            url = "https://www.toutiao.com/search_content/?offset="+str(i*20)+"&format=json&keyword={}&autoload=true&count=20&cur_tab=1&from=search_tab".format(key)
            headers = {
                'cookie':'tt_webid=6554563661830604302; WEATHER_CITY=%E5%8C%97%E4%BA%AC; UM_distinctid=16352d801092ac-0eb0a388034e86-33657f07-1fa400-16352d8010afee; tt_webid=6554563661830604302; uuid="w:a904362acac346dbb2b84c78fd6a868f"; sso_login_status=0; __tasessionId=jzofko4e61526271796330; CNZZDATA1259612802=102088705-1526101527-%7C1526270226',
                'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
                'x-requested-with':'XMLHttpRequest',
            }
            yield scrapy.Request(url,callback=self.Read_url,headers=headers,meta={'keys':key})

    def Read_url(self,response):
        print(response.url)
        f = json.loads(response.text)
        it = CcItem()
        content = f['data']
        data = []
        for li in content:
            try:
                print(li['id'],li['title'])
                url = "https://www.toutiao.com/a{}/".format(li['id'])
                it['key'] = response.meta['keys']
                data.append((li['title'],url))
                it['name'] = data
            except KeyError:
                print("暂无id")
        yield it


"""
auto registration the python module that under the rest folder python files
"""
import importlib
import os

from component import flask_blueprint

rest_dir_name = 'rest'
ignore_dir_list = ['__pycache__']
file_tag = '.py'

def confirm_file_tag():
    if not os.path.exists('setup.py'):
        flask_blueprint.file_tag = '.pyc'

def auto_registration_flask_blueprint(blueprint_path, module_name):
    from __init__ import app
    for item in os.listdir(blueprint_path):
        if item in ignore_dir_list:
            continue
        current_level_path = os.path.join(blueprint_path, item)
        cur_module_name = module_name + '.' + item
        if os.path.isdir(current_level_path):
            auto_registration_flask_blueprint(current_level_path, cur_module_name)
            continue
        if not cur_module_name.endswith(flask_blueprint.file_tag):
            continue
        cur_module_name = cur_module_name[:(0 - len(flask_blueprint.file_tag))]
        target_dynamic_module = importlib.import_module(cur_module_name)
        if hasattr(target_dynamic_module, 'app'):
            app.register_blueprint(target_dynamic_module.app)

def do_init():
    confirm_file_tag()
    auto_registration_flask_blueprint(rest_dir_name, rest_dir_name)


import csv
import json
import logging
import os
import time
import unittest
from bisect import bisect
from collections import OrderedDict
from datetime import datetime
from glob import glob
from numbers import Number
from pprint import pformat

import javaobj
import numpy as np
import pandas as pd
import psycopg2
import requests
from dateutil import parser
from xlrd import XLRDError

from ..test_base import AssetManagementUnitTest

logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.INFO)

class AssetManagementIntegrationTest(AssetManagementUnitTest):
    def setUp(self):
        self.amhost = 'localhost'
        self.refdes = 'CE04OSPS-SF01B-2A-CTDPFA107'
        self.conn = psycopg2.connect(host='localhost', database='metadata', user='awips')
        self.maxDiff = None

    @classmethod
    def setUpClass(cls):
        """
        Read bulk load asset management data and save UID and serial numbers
        :return:
        """
        bulk_dataframes = []
        for filename in glob(cls.BULK_FILES_GLOB):
            if 'unclassified' in filename:
                continue
            df = pd.read_csv(filename, na_values='', keep_default_na=False)
            df.dropna(how='all', inplace=True)
            bulk_dataframes.append(df)

        cls.bulk_data = pd.concat(bulk_dataframes)
        cls.bulk_data.rename(columns=cls.BULK_COLS_RENAME, inplace=True)
        cls.bulk_data.dropna(how='all', inplace=True)

        cls.cruise_data = pd.read_csv(cls.CRUISE_FILE)

    def rename_bulk_entry(self, data):
        self.am_name_map = {
            'modelNumber': 'model_number',
            'purchasePrice': 'purchase_price',
            'purchaseDate': 'purchase_date',
            'serialNumber': 'serial_number',
            'firmwareVersion': 'firmware_version',
            'assetType': 'asset_type',
        }

        out = {}
        for k, v in data.iteritems():
            k = self.am_name_map.get(k, k)

            if isinstance(v, unicode):
                v = str(v)
            elif v is None:
                v = ''
            elif self.isnan(v):
                v = ''
            if k == 'asset_type' and v == 'Mooring':
                v = 'Platform'
            out[k] = v
        return out

    @staticmethod
    def get_asset_record(uid):
        return requests.get('http://localhost:12587/asset', params={'uid': uid}).json()

    @staticmethod
    def get_deployment_record(refdes, deployment):
        subsite, node, sensor = refdes.split('-', 2)
        return requests.get('http://localhost:12587/events/deployment/inv/%s/%s/%s/%s' %
                            (subsite, node, sensor, deployment)).json()

    @staticmethod
    def build_cal_value(data):
        return data['value']

    @staticmethod
    def build_cal_timestamp(data):
        return datetime.utcfromtimestamp(data['eventStartTime'] / 1000)

    def get_cal_dict(self, uid):
        asset = self.get_asset_record(uid)
        cal_dict = {}
        for calibration in asset.get('calibration', []):
            name = calibration['name']
            data = calibration['calData']
            for each in data:
                notes = each['comments']
                value = self.build_cal_value(each)
                timestamp = self.build_cal_timestamp(each)
                cal_dict.setdefault(timestamp, {})[name] = value, notes
        return cal_dict

    def assert_bulk_entry(self, row):
        # retrieve the asset
        asset = self.get_asset_record(row['uid'])
        asset_map = self.rename_bulk_entry(asset)

        keys = set(row).intersection(set(asset_map))

        asset_map = {k: v for k, v in asset_map.iteritems() if k in keys}
        row_map = {k: v for k, v in row.iteritems() if k in keys}
        self.assertDictEqual(asset_map, row_map)

    def assert_cal_entry(self, df, uid, timestamp):
        cal_dict = self.get_cal_dict(uid)
        cal_times = sorted(cal_dict)
        cal_index = bisect(cal_times, timestamp)

        self.assertNotEqual(cal_index, 0, msg='No calibration data for UID (%s) at timestamp (%s)' % (uid, timestamp))

        my_cal = cal_dict[cal_times[cal_index-1]]
        for serial, name, value, notes in df.itertuples(index=False):
            found = my_cal.get(name)
            self.assertIsNotNone(found, msg='No calibration entry found for %s for UID (%s) at timestamp (%s) %r'
                                            % (name, uid, timestamp, my_cal.keys()))
            found_value, found_notes = found

            if self.isnan(notes):
                notes = None

            if isinstance(value, basestring):
                if value.startswith('SheetRef:'):
                    # TODO - read sheetref and verify values
                    continue
                else:
                    value = json.loads(value)

            np.testing.assert_almost_equal(found_value, value)
            self.assertEqual(found_notes, notes)

    def skip_invalid_deployment(self, row):
        return self.isnan(row['mooring.uid']) or \
            self.isnan(row['sensor.uid']) or \
            self.isnan(row['startDateTime'])

    def assert_deployment_entry(self, row):
        # These invalid records are already caught by the unit tests
        # skip them here so we can test all valid records
        if self.skip_invalid_deployment(row):
            return

        row_dict = row.to_dict()
        for k, v in row_dict.iteritems():
            if self.isnan(v):
                row_dict[k] = None

        refdes = row_dict['Reference Designator']
        dnum = row_dict['deploymentNumber']

        deployment = self.get_deployment_record(refdes, dnum)
        self.assertEqual(len(deployment), 1, msg='Wrong number of deployments found %s %s %d' %
                                                 (refdes, dnum, len(deployment)))
        deployment = deployment[0]

        deploy_cruise = deployment.get('deployCruiseInfo')
        recover_cruise = deployment.get('recoverCruiseInfo')
        mooring = deployment.get('mooring')
        node = deployment.get('node')
        sensor = deployment.get('sensor')
        deploy_cruise = None if deploy_cruise is None else deploy_cruise['uniqueCruiseIdentifier']
        recover_cruise = None if recover_cruise is None else recover_cruise['uniqueCruiseIdentifier']
        mooring = None if mooring is None else mooring['uid']
        node = None if node is None else node['uid']
        sensor = None if sensor is None else sensor['uid']

        location = deployment.get('location')
        location = {} if location is None else location
        lat = location.get('latitude')
        lon = location.get('longitude')
        orbit = location.get('orbitRadius')
        depth = location.get('depth')
        water_depth = deployment.get('waterDepth')

        try:
            self.assertEqual(deploy_cruise, row_dict['CUID_Deploy'])
            self.assertEqual(deployment.get('deployedBy'), row_dict['deployedBy'])
            self.assertEqual(recover_cruise, row_dict['CUID_Recover'])
            self.assertEqual(deployment.get('recoveredBy'), row_dict['recoveredBy'])
            self.assertEqual(deployment.get('eventName'), row_dict['Reference Designator'])
            self.assertEqual(deployment.get('deploymentNumber'), row_dict['deploymentNumber'])
            self.assertEqual(deployment.get('versionNumber'), row_dict['versionNumber'])
            self.assertEqual(deployment.get('eventStartTime'), row_dict['startDateTime'])
            self.assertEqual(deployment.get('eventStopTime'), row_dict['stopDateTime'])
            self.assertEqual(mooring, row_dict['mooring.uid'])
            self.assertEqual(node, row_dict['node.uid'])
            self.assertEqual(sensor, row_dict['sensor.uid'])
            np.testing.assert_almost_equal(lat, row_dict['lat'])
            np.testing.assert_almost_equal(lon, row_dict['lon'])
            self.assertEqual(orbit, row_dict['orbit'])
            self.assertEqual(depth, row_dict['deployment_depth'])
            self.assertEqual(water_depth, row_dict['water_depth'])
        except AssertionError:
            log.error(pformat(row_dict))
            log.error(pformat(deployment))
            raise

    def test_bulk_load(self):
        df = self.bulk_data
        df = df.fillna({'mobile': 0})
        df['mobile'] = df.mobile.values.astype(bool)
        df['purchase_date'] = map(self.date_to_millis, df.purchase_date.values)
        df['purchase_price'] = map(self.maybe_float, df.purchase_price.values)
        df = df.fillna('')
        for _, row in df.iterrows():
            self.assert_bulk_entry(row.to_dict())

    def test_cal_load(self):
        for path in self.walk_cal_files():
            uid, timestamp = self.parse_cal_filename(path)
            dt = parser.parse(timestamp)
            df = self.parse_cal_file(path)
            self.assert_cal_entry(df, uid, dt)

    def test_deployment_load(self):
        for path in self.walk_deployment_files():
            df = self.parse_dep_file(path)
            for _, row in df.iterrows():
                self.assert_deployment_entry(row)

    # def assert_cal_times(self, calibrations, asset, dstart, dstop):
    #     if not calibrations:
    #         return
    #
    #     # verify that one of the calibrations is valid for this deployment
    #     cal_dict = {}
    #     for cal in calibrations:
    #         name = cal['name']
    #         # print name, len(cal['calData'])
    #         for each in cal['calData']:
    #             cstart = each['eventStartTime']
    #
    #             cal_asset = each['assetUid']
    #             self.assertEqual(asset, cal_asset)
    #             # print name, cal_asset, cstart
    #             cal_dict.setdefault(name, []).append(cstart)
    #
    #     # assert that there are an equal number of cals for each deployment
    #     lengths = {len(x) for x in cal_dict.values()}
    #     if len(lengths) != 1:
    #         print 'missing cal values? asset: %s cals: %s' % (asset, cal_dict)
    #     # self.assertEqual(len(lengths), 1, msg='missing cal values? asset: %s cals: %s' % (asset, cal_dict))
    #     import pprint
    #     pprint.pprint(cal_dict)
    #
    # def assert_events(self, events):
    #     self.assertEqual(len(events), 1)
    #     now = time.time() * 1000
    #     deployment = events[0]
    #     dstart = deployment['eventStartTime']
    #     dstop = deployment['eventStopTime']
    #     sensor = deployment['sensor']
    #     asset = sensor['uid']
    #     calibrations = sensor['calibration']
    #     if not dstop:
    #         dstop = now
    #
    #     self.assert_cal_times(calibrations, asset, dstart, dstop)
    #
    # def test_all_cals(self):
    #     import requests
    #     base_url = 'http://localhost:12587/events/deployment/inv'
    #     subsites = requests.get(base_url).json()
    #     for subsite in subsites:
    #         nodes = requests.get('%s/%s' % (base_url, subsite)).json()
    #         for node in nodes:
    #             sensors = requests.get('%s/%s/%s' % (base_url, subsite, node)).json()
    #             for sensor in sensors:
    #                 deployments = requests.get('%s/%s/%s/%s' % (base_url, subsite, node, sensor)).json()
    #                 for deployment in deployments:
    #                     events = requests.get('%s/%s/%s/%s/%d' % (base_url, subsite, node, sensor, deployment)).json()
    #                     print subsite, node, sensor, deployment
    #                     self.assert_events(events)
    #         break
    #         # print subsite, node, sensor, deployment, events
    #     assert subsites is False
    #
    # def make_array(self, dimensions, values):
    #     if dimensions == [1] and len(values) == 1:
    #         return values[0]
    #
    #     a = np.array(values)
    #     return list(np.reshape(a, dimensions))
    #
    # def assert_cal_value(self, refdes, depnum, uid, start, name, value, adj_time=False):
    #     # print repr(uid), start, repr(name), repr(value)
    #     cur = self.conn.cursor()
    #     never = False
    #
    #     if adj_time:
    #         cur.execute('''
    #         select eventstarttime/1000
    #             from xasset xa, xdeployment xd, xevent xe
    #             where xa.assetid=xd.sassetid and xd.eventid=xe.eventid and xa.uid=%s and eventstarttime >= %s
    #             order by eventstarttime limit 1''', (uid, start * 1000))
    #         row = cur.fetchone()
    #         if row:
    #             start = row[0]
    #         else:
    #             never = True
    #
    #     cur.execute(
    #         'select name, dimensions, values, eventstarttime from xcalibration xc, xcalibrationdata xcd, xevent xe where xc.calid = xcd.calid and xe.eventid = xcd.eventid and assetuid=%s and name=%s',
    #         (uid, name))
    #     rows = cur.fetchall()
    #     values = []
    #     for name, dims, vals, t in rows:
    #         try:
    #             if dims is not None and vals is not None:
    #                 dims = javaobj.loads(dims)
    #                 vals = javaobj.loads(vals)
    #                 a = self.make_array(dims, vals)
    #                 t /= 1000.0
    #                 values.append((t, a))
    #         except IOError:
    #             print name, repr(dims), repr(vals)
    #
    #     rvalues = [datetime.utcfromtimestamp(x).isoformat() for x, y in values]
    #
    #     if not values and never:
    #         rval = 'neverdeployed', refdes, depnum, name, uid, start, datetime.utcfromtimestamp(
    #             start).isoformat(), str(value)[:20], rvalues
    #         print rval
    #         return rval
    #
    #     if not values:
    #         rval = 'missing', refdes, depnum, name, uid, start, datetime.utcfromtimestamp(start).isoformat(), str(value)[:20], rvalues
    #         print rval
    #         return rval
    #
    #     values.sort()
    #     times = [v[0] for v in values]
    #     index = bisect(times, start)
    #
    #     if index <= 0:
    #         rval = 'notfound', refdes, depnum, name, uid, start, datetime.utcfromtimestamp(start).isoformat(), str(value)[:20], rvalues
    #         print rval
    #         return rval
    #
    #     t, expected = values[index - 1]
    #     try:
    #         np.testing.assert_array_almost_equal(expected, value)
    #     except AssertionError:
    #         rval = 'mismatch', refdes, depnum, name, uid, start, datetime.utcfromtimestamp(start).isoformat(), str(value)[:20], rvalues
    #         print rval
    #         return rval
    #
    # def test_new_spreadsheets(self):
    #     import pandas as pd
    #     unix_epoch = datetime(1970, 1, 1)
    #     results = []
    #     for filename in glob('/Users/petercable/src/asset-management/ARCHIVE/refactored_asset_management/Calibration/*xlsx'):
    #         print filename
    #         sheets = pd.read_excel(filename, sheetname=None)
    #         cal_sheets = [sheet for sheet in sheets if 'Cal_Info' in sheet]
    #         for sheet in cal_sheets:
    #             df = sheets[sheet]
    #             try:
    #                 df = df[
    #                     ['Sensor.uid', 'startDateTime', 'Calibration Cofficient Name', 'Calibration Cofficient Value']]
    #                 df = df.dropna(how='all')
    #                 for each in df.itertuples(index=False):
    #                     uid, start, name, value = each
    #                     if (isinstance(uid, float) and np.isnan(uid) or
    #                                 isinstance(name, float) and np.isnan(name) or
    #                                 isinstance(value, float) and np.isnan(value)):
    #                         continue
    #
    #                     if isinstance(value, basestring):
    #                         try:
    #                             value = json.loads(value)
    #                         except ValueError:
    #                             continue
    #                     start = (start - unix_epoch).total_seconds()
    #                     result = self.assert_cal_value(None, None, uid, start, name, value, adj_time=True)
    #                     if result:
    #                         results.append(result)
    #             except (XLRDError):
    #                 pass
    #
    #     print len(results)
    #     with open('new_results.csv', 'w') as fh:
    #         writer = csv.writer(fh)
    #         writer.writerows(results)
    #     assert False
    #
    # def test_old_spreadsheets(self):
    #     skip = ['CC_lat', 'CC_lon', 'CC_latitude', 'CC_longitude', 'Inductive ID', 'Induction ID', 'CC_depth']
    #     unix_epoch = datetime(1970, 1, 1)
    #     results = []
    #     count = 0
    #     for filename in glob('/Users/petercable/src/asset-management/ARCHIVE/deployment/Omaha_Cal*xlsx'):
    #         print filename
    #         sheets = pd.read_excel(filename, sheetname=None)
    #         if 'Moorings' in sheets and 'Asset_Cal_Info' in sheets:
    #             moorings = sheets['Moorings']
    #             moorings = moorings[['Mooring OOIBARCODE', 'Deployment Number', 'Anchor Launch Date', 'Anchor Launch Time']]
    #             moorings = moorings.dropna(how='any')
    #             deployments = {}
    #
    #             for row in moorings.itertuples(index=False):
    #                 barcode, number, date, rowtime = row
    #
    #                 if date == rowtime:
    #                     dt = date
    #                 else:
    #                     if isinstance(time, datetime):
    #                         rowtime = rowtime.time()
    #                     dt = datetime.combine(date.date(), rowtime)
    #
    #                 deployments[(barcode, int(number))] = dt
    #
    #             all_moorings = list({x[0] for x in deployments})
    #
    #             df = sheets['Asset_Cal_Info']
    #             try:
    #                 df = df[
    #                     ['Ref Des', 'Mooring OOIBARCODE', 'Sensor OOIBARCODE', 'Deployment Number',
    #                      'Calibration Cofficient Name', 'Calibration Cofficient Value']]
    #                 df = df.dropna(how='all')
    #                 for each in df.itertuples(index=False):
    #                     refdes, mooring_uid, uid, deployment, name, value = each
    #
    #                     if name in skip:
    #                         continue
    #
    #                     if mooring_uid is None or isinstance(mooring_uid, float) and np.isnan(mooring_uid):
    #                         if len(all_moorings) == 1:
    #                             mooring_uid = all_moorings[0]
    #
    #                     start = deployments.get((uid, deployment))
    #                     if start is None:
    #                         start = deployments.get((mooring_uid, deployment))
    #                     if start is None:
    #                         print 'deployment not found', each, deployments
    #                         continue
    #
    #                     if (isinstance(uid, float) and np.isnan(uid) or
    #                                 isinstance(name, float) and np.isnan(name) or
    #                                 isinstance(value, float) and np.isnan(value)):
    #                         continue
    #
    #                     if isinstance(value, basestring):
    #                         try:
    #                             value = json.loads(value)
    #                         except ValueError:
    #                             continue
    #                     start = (start - unix_epoch).total_seconds()
    #                     count += 1
    #                     result = self.assert_cal_value(refdes, deployment, uid, start, name, value)
    #                     if result:
    #                         results.append(result)
    #             except (XLRDError, KeyError) as e:
    #                 print e
    #
    #     print len(results)
    #     print count
    #     with open('old_results.csv', 'w') as fh:
    #         writer = csv.writer(fh)
    #         writer.writerow(('reason', 'refdes', 'deployment', 'name', 'uid', 'start', 'start_ts', 'value', 'retrieved'))
    #         writer.writerows(results)
    #
    #     assert False


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import unittest
import os
import shutil
import tempfile
import difflib
import datetime
from mock import patch

from spec_cleaner import RpmSpecCleaner

class TestCompare(unittest.TestCase):

    """
    We run individual tests to verify the content compared to expected results
    """

    def setUp(self):
        """
        Declare global scope variables for further use.
        """

        self.input_dir = self._get_input_dir()
        self.fixtures_dir = self._get_fixtures_dir()
        self.minimal_fixtures_dir = self._get_minimal_fixtures_dir()
        self.tmp_dir = tempfile.mkdtemp()
        self.tmp_file_rerun = tempfile.NamedTemporaryFile()

    def tearDown(self):
        """
        Remove the tmp directory
        """
        shutil.rmtree(self.tmp_dir)

    def _difftext(self, lines1, lines2, junk=None):
        junk = junk or (' ', '\t')
        # result is a generator
        result = difflib.ndiff(lines1, lines2, charjunk=lambda x: x in junk)
        read = []
        for line in result:
            read.append(line)
            # lines that don't start with a ' ' are diff ones
            if not line.startswith(' '):
                self.fail(''.join(read + list(result)))

    def assertStreamEqual(self, stream1, stream2, junk=None):
        """compare two streams (using difflib and readlines())"""
        # if stream2 is stream2, readlines() on stream1 will also read lines
        # in stream2, so they'll appear different, although they're not
        if stream1 is stream2:
            return
        # make sure we compare from the beginning of the stream
        stream1.seek(0)
        stream2.seek(0)
        # ocmpare
        self._difftext(stream1.readlines(), stream2.readlines(), junk)

    def _get_input_dir(self):
        """
        Return path for input files used by tests
        """
        return os.path.join(os.getcwd(), 'tests/in/')

    def _get_fixtures_dir(self):
        """
        Return path for representative output specs
        """
        return os.path.join(os.getcwd(), 'tests/out/')

    def _get_minimal_fixtures_dir(self):
        """
        Return path for representative output specs
        """
        return os.path.join(os.getcwd(), 'tests/out-minimal/')

    def _obtain_list_of_tests(self):
        """
        Generate list of tests we are going to use according to what is on hdd
        """
        test_files = list()

        for spec in os.listdir(self.fixtures_dir):
            if spec.endswith(".spec"):
                test_files.append(spec)

        return test_files

    def _run_individual_test(self, options):
        """
        Run the cleaner as specified and store the output for further comparison.
        """
        cleaner = RpmSpecCleaner(options)
        cleaner.run()

    @patch('spec_cleaner.rpmcopyright.datetime')
    def test_input_files(self, datetime_mock):
        datetime_mock.datetime.now.return_value = (
            datetime.datetime(2013, 1, 1))
        for test in self._obtain_list_of_tests():
            infile = os.path.join(self.input_dir, test)
            compare = os.path.join(self.fixtures_dir, test)
            tmp_file = os.path.join(self.tmp_dir, test)

            # first try to generate cleaned content from messed up
            options = {
                'specfile': infile,
                'output': tmp_file,
                'pkgconfig': True,
                'inline': False,
                'diff': False,
                'diff_prog': 'vimdiff',
                'minimal': False,
                'no_copyright': False,
            }
            self._run_individual_test(options)
            with open(compare) as ref, open(tmp_file) as test:
                self.assertStreamEqual(ref, test)

            # second run it again while ensuring it didn't change
            options = {
                'specfile': tmp_file,
                'output': self.tmp_file_rerun.name,
                'pkgconfig': True,
                'inline': False,
                'diff': False,
                'diff_prog': 'vimdiff',
                'minimal': False,
                'no_copyright': False,
            }
            self._run_individual_test(options)
            with open(compare) as ref, open(self.tmp_file_rerun.name) as test:
                self.assertStreamEqual(ref, test)

    @patch('spec_cleaner.rpmcopyright.datetime')
    def test_minimal_output(self, datetime_mock):
        datetime_mock.datetime.now.return_value = (
            datetime.datetime(2013, 1, 1))
        for test in self._obtain_list_of_tests():
            infile = os.path.join(self.input_dir, test)
            compare = os.path.join(self.minimal_fixtures_dir, test)
            tmp_file = os.path.join(self.tmp_dir, test)

            # first try to generate cleaned content from messed up
            options = {
                'specfile': infile,
                'output': tmp_file,
                'pkgconfig': True,
                'inline': False,
                'diff': False,
                'diff_prog': 'vimdiff',
                'minimal': True,
                'no_copyright': False,
            }
            self._run_individual_test(options)
            with open(compare) as ref, open(tmp_file) as test:
                self.assertStreamEqual(ref, test)

            # second run it again while ensuring it didn't change
            options = {
                'specfile': tmp_file,
                'output': self.tmp_file_rerun.name,
                'pkgconfig': True,
                'inline': False,
                'diff': False,
                'diff_prog': 'vimdiff',
                'minimal': True,
                'no_copyright': False,
            }
            self._run_individual_test(options)
            with open(compare) as ref, open(self.tmp_file_rerun.name) as test:
                self.assertStreamEqual(ref, test)

    @patch('spec_cleaner.rpmcopyright.datetime')
    def test_no_copyright_output(self, datetime_mock):
        datetime_mock.datetime.now.return_value = (
            datetime.datetime(2013, 1, 1))
        spec_str="""%check
make check

%changelog
"""
        tmp_file = os.path.join(self.tmp_dir, "no_copyright_test.spec")
        out_file = os.path.join(self.tmp_dir, "no_copyright_test_out.spec")
        with open(tmp_file, "w+") as t:
            t.write(spec_str)

        # first try to generate cleaned content from messed up
        options = {
            'specfile': tmp_file,
            'output': out_file,
            'pkgconfig': True,
            'inline': False,
            'diff': False,
            'diff_prog': 'vimdiff',
            'minimal': True,
            'no_copyright': True,
        }
        self._run_individual_test(options)
        with open(out_file) as ref, open(tmp_file) as test:
            self.assertStreamEqual(ref, test)

    @patch('spec_cleaner.rpmcopyright.datetime')
    def test_inline_function(self, datetime_mock):
        datetime_mock.datetime.now.return_value = (
            datetime.datetime(2013, 1, 1))

        test = self._obtain_list_of_tests()[0]
        infile = os.path.join(self.input_dir, test)
        compare = os.path.join(self.fixtures_dir, test)
        tmp_file = os.path.join(self.tmp_dir, test)
        shutil.copyfile(infile, tmp_file)

        options = {
            'specfile': tmp_file,
            'output': '',
            'pkgconfig': True,
            'inline': True,
            'diff': False,
            'diff_prog': 'vimdiff',
            'minimal': False,
            'no_copyright': False,
        }
        self._run_individual_test(options)
        with open(compare) as ref, open(tmp_file) as test:
            self.assertStreamEqual(ref, test)

    @patch('spec_cleaner.rpmcopyright.datetime')
    def test_regular_output(self, datetime_mock):
        datetime_mock.datetime.now.return_value = (
            datetime.datetime(2013, 1, 1))

        test = self._obtain_list_of_tests()[0]
        infile = os.path.join(self.input_dir, test)
        options = {
            'specfile': infile,
            'output': '',
            'pkgconfig': True,
            'inline': False,
            'diff': False,
            'diff_prog': 'gvimdiff',
            'minimal': False,
            'no_copyright': False,
        }
        self._run_individual_test(options)

    @patch('spec_cleaner.rpmcopyright.datetime')
    @patch('subprocess.call')
    def test_diff_function(self, datetime_mock, subprocess_mock):
        datetime_mock.datetime.now.return_value = (
            datetime.datetime(2013, 1, 1))
        subprocess_mock.subprocess.call.return_value = True

        test = self._obtain_list_of_tests()[0]
        infile = os.path.join(self.input_dir, test)
        options = {
            'specfile': infile,
            'output': '',
            'pkgconfig': True,
            'inline': False,
            'diff': True,
            'diff_prog': 'gvimdiff',
            'minimal': False,
            'no_copyright': False,
        }
        self._run_individual_test(options)


# encoding: utf-8

import os
import math
import json
import threading
import subprocess
from numbers import Integral
from distutils import spawn

import numpy as np

from .py23 import TemporaryDirectory, Queue
from .utils import assert_normal
from .belief import (ArrayBelief, ParticleBelief, MaxSamplesReached,
                     format_belief_array)

SOLVER_NAME = 'pomdp-solve'

class ValueFunctionParseError(ValueError):
    pass

class Impossible(ValueError):
    pass

def parse_value_function(reader):
    has_action = False
    actions = []
    vectors = []
    for line in reader:
        if not line.isspace():
            if has_action:
                # expect vector
                vectors.append(np.fromstring(line, sep=' '))
                has_action = False
            else:
                # expect action
                actions.append(int(line))
                has_action = True
        # else: skip line
    if has_action:
        raise ValueFunctionParseError('Action defined but no vectors follows.')
    return actions, np.vstack(vectors)

def parse_policy_graph(reader):
    actions = []
    transitions = []
    for i, line in enumerate(reader):
        line = line.rstrip('\n').rstrip()
        if not line.isspace():
            # 'N A  Z1 Z2 Z3'.split(' ') -> ['N', 'A', '', 'Z1', 'Z2', 'Z3']
            l = line.split(' ')
            n = int(l[0])  # Node name
            assert(n == i)
            actions.append(int(l[1]))
            transitions.append([None if x == '-' else int(x) for x in l[3:]])
    return actions, transitions

PREAMBLE_FMT = """discount: {discount}
values: reward
states: {states}
actions: {actions}
observations: {observations}
"""
DECIMALS = 5
NUMBER_FORMAT = '{:0.' + str(DECIMALS) + 'f}'

def _as_list(lst_or_int):
    if isinstance(lst_or_int, int):
        return list(range(lst_or_int))
    else:
        return lst_or_int

def _dump_list(lst):
    return ' '.join([str(x) for x in lst])

def _dump_list_or_count(lst_or_int):
    if isinstance(lst_or_int, int):
        return str(lst_or_int)
    else:
        return _dump_list(lst_or_int)

def _dump_1d_array(a):
    # Make sure that sum stays the same even after trunc
    trunc_sum = np.around(a.sum(), decimals=DECIMALS)
    trunc = np.around(a, decimals=DECIMALS)
    imax = np.argmax(trunc)
    # Compensate on max to avoid negative values
    trunc[imax] += trunc_sum - trunc.sum()
    return ' '.join([NUMBER_FORMAT.format(x) for x in trunc])

def _dump_2d_array(a):
    return '\n'.join([_dump_1d_array(x) for x in a])

def _dump_3d_array(a, name, xs):
    """Dump a 3d array for a POMDP file.

    :param a: the array
    :param name: the name of the array in the file
    :param xs: names of the first dimension
    """
    return '\n'.join([
        "{} : {}\n{}".format(name, x, _dump_2d_array(a[ix, :, :]))
        for ix, x in enumerate(xs)
        ])

def _dump_4d_array(a, name, xs, ys):
    """Dump a 4d array for a POMDP file.

    :param a: the array
    :param name: the name of the array in the file
    :param xs: names of the first dimension
    :param ys: names of the second dimension
    """
    return '\n'.join([
        _dump_3d_array(a[ix, :, :, :], name,
                       ["{} : {}".format(x, y) for y in ys])
        for ix, x in enumerate(xs)
        ])

class POMDP:

    """Partially observable Markov model.

    :param T: array of shape (n_actions, n_states, n_states)
        Transition probabilities (must sum to 1 on last dimension)
    :param O: array of shape (n_actions, n_states, n_observations)
        Observation probabilities (action, *end state*, observation)
        (must sum to 1 on last dimension)
    :param R: array of shape (n_actions, n_states, n_states, n_observations)
        Rewards or cost (must sum to 1 on last dimension)
    :param start: array of shape (n_states)
        Initial state probabilities
    :param discount: discount factor (int)
    :param states: None | iterable of states
        Default to range(n_states).
    :param actions: None | iterable of actions
        Default to range(n_actions).
    :param observations: None | iterable of observations
        Default to range(n_observations).
    :values: ('reward' | 'cost')
        How to interpret reward coefficients.
    :solver_path: string
        Path in which to look for the executable (default to $PATH)
    """

    def __init__(self, T, O, R, start, discount, states=None, actions=None,
                 observations=None, values='reward', solver_path=None):
        # Defaults for actions, states and observations
        a, s, o = O.shape
        self._init_states(states, s)
        self._init_actions(actions, a)
        self._init_observations(observations, o)
        self.T = T
        self.O = O
        if values == 'reward':
            self.R = R
        elif values == 'cost':
            self.R = -R
        else:
            raise ValueError(
                "Values must be 'reward' of 'cost. Got '{}'.".format(values))
        self.start = start
        self._assert_shapes()
        self._assert_normal()
        self._assert_unique()
        if discount > 1 or discount < 0:
            raise ValueError('Discount factor must be ≤ 1 and ≥ 0.')
        self.discount = discount
        self._solver_path = spawn.find_executable(SOLVER_NAME,
                                                  path=solver_path)
        if self._solver_path is None:
            raise ImportError('Could not find executable for pomdp-solve.')

    def _init_states(self, states, s):
        if states is not None:
            self._s = list(states)
        else:
            self._s = s

    def _init_actions(self, actions, a):
        if actions is not None:
            self._a = list(actions)
        else:
            self._a = a

    def _init_observations(self, observations, o):
        if observations is not None:
            self._o = list(observations)
        else:
            self._o = o

    @property
    def states(self):
        return _as_list(self._s)

    @property
    def actions(self):
        return _as_list(self._a)

    @property
    def observations(self):
        return _as_list(self._o)

    @property
    def n_states(self):
        return len(self.states)

    @property
    def n_actions(self):
        return len(self.actions)

    @property
    def n_observations(self):
        return len(self.observations)

    def _assert_shapes(self):
        s = self.n_states
        a = self.n_actions
        o = self.n_observations
        message = "Wrong shape for {}: got {}, expected {}."

        def assert_shape(array, name, shape):
            if array.shape != shape:
                raise ValueError(message.format(name, array.shape, shape))

        assert_shape(self.start, 'start', (s,))
        assert_shape(self.T, 'T', (a, s, s))
        assert_shape(self.O, 'O', (a, s, o))
        assert_shape(self.R, 'R', (a, s, s, o))

    def _assert_normal(self):
        assert_normal(self.start, name='start')
        assert_normal(self.T, name='T')
        assert_normal(self.O, name='O')

    def _assert_unique(self):
        message = "Found duplicate {}: {}"

        def assert_no_dup(lst, name):
            if not len(set(lst)) == len(lst):
                dup = list(lst)
                for a in set(lst):
                    dup.remove(a)
                raise ValueError(message.format(name, dup))

        assert_no_dup(self.states, 'states(s)')
        assert_no_dup(self.actions, 'action(s)')
        assert_no_dup(self.observations, 'observation(s)')

    def belief_update(self, a, o, b):
        new_b = b.dot(self.T[a, ...]) * self.O[a, :, o]
        s = new_b.sum()
        if s == 0.:
            raise Impossible('Impossible observation: ' + str(o))
        return new_b / s

    def sample_transition(self, a, s):
        new_s = np.random.choice(self.n_states, p=self.T[a, s, :])
        o = np.random.choice(self.n_observations, p=self.O[a, new_s, :])
        r = self.R[a, s, new_s, o]
        return new_s, o, r

    def sample_start(self):
        return np.random.choice(self.n_states, p=self.start)

    def dump(self):
        """Write POMDP description following:
        `<http://www.pomdp.org/code/pomdp-file-spec.html>`_
        """
        preamble = PREAMBLE_FMT.format(
            discount=self.discount,
            states=_dump_list_or_count(self._s),
            actions=_dump_list_or_count(self._a),
            observations=_dump_list_or_count(self._o))
        start = "start: {}".format(_dump_1d_array(np.asarray(self.start)))
        T = _dump_3d_array(self.T, 'T', self.actions)
        O = _dump_3d_array(self.O, 'O', self.actions)
        R = _dump_4d_array(self.R, 'R', self.actions, self.states)
        return '\n\n'.join([preamble, start, T, O, R])

    def dump_to(self, path, name):
        full_path = os.path.join(path, name + '.pomdp')
        with open(full_path, 'w') as f:
            f.write(self.dump())
        return full_path

    def to_dict(self):
        return {'T': self.T.tolist(),
                'O': self.O.tolist(),
                'R': self.R.tolist(),
                'start': self.start.tolist(),
                'discount': self.discount,
                'states': self.states,
                'actions': self.actions,
                'observations': self.observations,
                }

    def as_json(self):
        return json.dumps(self.to_dict())

    def save_as_json(self, path):
        with open(path, 'w') as f:
            json.dump(self.to_dict(), f)

    @classmethod
    def from_dict(cls, d):
        return cls(np.asarray(d['T']), np.asarray(d['O']), np.asarray(d['R']),
                   np.asarray(d['start']), d['discount'], states=d['states'],
                   actions=d['actions'], observations=d['observations'],
                   values='reward')

    @classmethod
    def from_json(cls, s):
        d = json.loads(s)
        return cls.from_dict(d)

    @classmethod
    def load_from_json(cls, path):
        with open(path) as f:
            d = json.load(f)
            return cls.from_dict(d)

    def randomize(self, p_unexpected=1.e-3):
        self.T += p_unexpected
        self.T /= self.T.sum(-1)[..., None]
        self.O += p_unexpected
        self.O /= self.O.sum(-1)[..., None]

    def solve(self, timeout=None, n_iterations=None, method='incprune',
              grid_type=None, seed=None, verbose=False):
        """
        :param method: incprune | grid (incprune)
        :param grid_type: simplex | pairwise (simplex)
        """
        name = 'tosolve'
        args = []
        if timeout is not None:
            args.extend(['-time_limit', str(timeout)])
        if n_iterations is not None:
            args.extend(['-horizon', str(n_iterations)])
        if seed is None:
            seed = np.random.randint(1.e10)
        args.extend(['-rand_seed', str(seed)])
        if method == 'grid':
            if grid_type is None:
                grid_type = 'simplex'
            args.extend(['-method', method, '-fg_type', grid_type])
        with TemporaryDirectory() as tmpdir:
            pomdp_file = self.dump_to(tmpdir, name)
            args.extend(['-o', name, '-pomdp', pomdp_file])
            with open(os.devnull, 'w') as DEVNULL:
                subprocess.check_call(
                    [self._solver_path] + args, cwd=tmpdir,
                    stdout=None if verbose else DEVNULL)
            return self.load_policy_from(tmpdir, name)

    def load_policy_from(self, path, name):
        value_function_file = os.path.join(path, name + '.alpha')
        policy_graph_file = os.path.join(path, name + '.pg')
        with open(value_function_file, 'r') as vf:
            actions, vf = parse_value_function(vf)
        with open(policy_graph_file, 'r') as pf:
            actions2, pg = parse_policy_graph(pf)
        assert(actions == actions2)
        assert(max(actions) < len(self.actions))
        assert(max([t for ts in pg for t in ts if t is not None]) <= len(pg))
        action_names = [self.actions[a] for a in actions]
        return GraphPolicy(action_names, self.observations, pg, vf,
                           start=self.start)

class GraphPolicy:

    def __init__(self, actions, observations, transitions, values, start=None,
                 init=None):
        self.actions = actions
        self.observations = observations
        self.transitions = np.asarray(transitions)
        assert(self.transitions.shape == (self.n_nodes, len(observations)))
        self.values = np.asarray(values)
        if init is not None:
            assert(init < self.n_nodes)
            self.init = init
        elif start is not None:
            self.init = self.get_node_from_belief(start)
        else:
            raise ValueError('Must specify either init node or start belief.')

    @property
    def n_nodes(self):
        return len(self.actions)

    def get_node_from_belief(self, b):
        return self.values.dot(b[:, np.newaxis]).argmax()

    def get_action(self, current):
        return self.actions[current]

    def next(self, current, observation):
        return self.transitions[current, self.observations.index(observation)]

    def to_dict(self):
        return {'actions': self.actions,
                'observations': self.observations,
                'transitions': self.transitions.tolist(),
                'values': self.values.tolist(),
                'initial': str(self.init),
                }

    def to_json(self, indent=None):
        return json.dumps(self.to_dict(), indent=indent)

    def save_as_json(self, path, indent=None):
        with open(path, 'w') as fp:
            json.dump(self.to_dict(), fp, indent=indent)

    @classmethod
    def from_dict(cls, d):
        return cls(d['actions'], d['observations'], d['transitions'],
                   d['values'], init=int(d['initial']))

    @classmethod
    def from_json(cls, s):
        return cls.from_dict(json.loads(s))

    @classmethod
    def load_from_json(cls, path):
        with open(path) as f:
            return cls.from_dict(json.load(f))

class GraphPolicyRunner(object):

    def __init__(self, graph_policy):
        self.gp = graph_policy
        self.reset()

    def reset(self, belief=None):
        if belief is not None:
            self.current = self.gp.get_node_from_belief(belief)
        else:
            self.current = self.gp.init

    def get_action(self):
        return self.gp.get_action(self.current)

    def step(self, observation):
        self.current = self.gp.next(self.current, observation)
        if self.current is None:
            raise Impossible('Got unexpected observation')

class GraphPolicyBeliefRunner(GraphPolicyRunner):

    def __init__(self, graph_policy, pomdp):
        self.gp = graph_policy
        self.pomdp = pomdp
        self.reset()

    def reset(self, belief=None):
        if belief is None:
            belief = self.pomdp.start
        self.current_belief = belief
        super(GraphPolicyBeliefRunner, self).reset(belief=belief)

    def step(self, observation):
        a = self.pomdp.actions.index(self.get_action())
        o = self.pomdp.observations.index(observation)
        b = self.pomdp.belief_update(a, o, self.current_belief)
        self.reset(belief=b)

    def _rec_trajectory_tree(self, obs, horizon):
        if horizon >= 0:
            try:
                b = self.current_belief
                self.step(obs)
                tree = self.trajectory_tree(horizon)
                self.reset(belief=b)  # Restore state for next obs
                return tree
            except Impossible:  # Observation is impossible here
                pass
        return None  # either horizon is reached or observation is impossible

    def trajectory_tree(self, horizon):
        obs = self.pomdp.observations
        children = [self._rec_trajectory_tree(o, horizon - 1) for o in obs]
        return {"belief": self.current_belief.tolist(),
                "action": self.get_action(),
                "node": int(self.current),
                "observations": [o for i, o in enumerate(obs)
                                 if children[i] is not None],
                "children": [c for c in children if c is not None],
                }

    def trajectory_trees_from_starts(self, horizon=5):
        start = self.pomdp.start
        trees = []
        for s in start.nonzero():
            b = np.zeros(start.shape)
            b[s] = 1.
            self.reset(belief=b)
            trees.append(self.trajectory_tree(horizon))
        return {"graphs": trees}

    def save_trajectories_from_starts(self, dest, horizon=5, indent=None):
        with open(dest, 'w') as f:
            json.dump(self.trajectory_trees_from_starts(horizon=horizon),
                      f, indent=indent)

    def visit(self, max_states=100):
        v = _Aux(self, max_nodes=max_states)
        v.visit()
        return GraphPolicy(v.actions, v.observations, np.asarray(v.trans),
                           np.vstack(v.nodes), init=0)

class _Aux:

    tol = 1.e-2

    def __init__(self, pgbr, max_nodes=100):
        self.pr = pgbr
        self.max_nodes = max_nodes
        self.nodes = []
        self.queue = Queue()  # FIFO
        self.trans = []
        self.actions = []

    @property
    def observations(self):
        return self.pr.pomdp.observations

    @property
    def beliefs(self):
        return np.vstack(self.nodes)

    def closest(self, b):
        if len(self.nodes) < 1:
            return -1, np.inf
        else:
            distances = np.sqrt(((self.beliefs - b) ** 2).sum(-1))
            i = distances.argmin()
            return i, distances[i]

    def index(self, b):
        i, d = self.closest(b)
        if d < self.tol:
            return i
        else:
            i = len(self.nodes)
            self.nodes.append(b)
            self.trans.append([None for _ in self.observations])
            self.pr.reset(np.array(b))
            self.actions.append(self.pr.get_action())
            self.queue.put(i)
            return i

    def visit(self):
        self.index(self.pr.pomdp.start)
        while not (self.queue.empty() or len(self.nodes) > self.max_nodes):
            ib = self.queue.get()
            for io, o in enumerate(self.observations):
                try:
                    self.pr.reset(belief=np.array(self.nodes[ib]))
                    self.pr.step(o)
                    ib_new = self.index(self.pr.current_belief)
                    self.trans[ib][io] = int(ib_new)
                except Impossible:
                    pass

# POMCP

class Horizon(object):

    class _Generator:

        def __init__(self, cls, *args, **kwargs):
            self.cls = cls
            self.args = args
            self.kwargs = kwargs

        def __call__(self):
            return self.cls(*self.args, **self.kwargs)

    def is_reached(self):
        raise NotImplementedError

    def decrement(self, a, s, new_s, o):
        raise NotImplementedError

    def copy(self):
        raise NotImplementedError

    @classmethod
    def generator(cls, model, **parameters):
        raise NotImplementedError

class NTransitionsHorizon(Horizon):

    def __init__(self, n):
        self.n = n

    def is_reached(self):
        return self.n <= 0

    def decrement(self, a, s, new_s, o):
        self.n -= 1

    def copy(self):
        return NTransitionsHorizon(self.n)

    @classmethod
    def generator(cls, model, n=100):
        return cls._Generator(cls, n)

def _children_to_dict(d, children):
    d['children'] = children
    return d

def _null_logger(*args, **kwargs):
    pass

class _SearchTree:

    rollout_it = 100

    def __init__(self, model, horizon_generator, exploration,
                 relative_exploration=False, belief='array', belief_params={},
                 node_params={}, logger=None):
        self._belief = belief
        self._belief_params = belief_params
        self.model = model
        self._node_params = node_params
        self.root = self._observation_node_for_belief(self._belief_start())
        self.horizon_gen = horizon_generator
        self.exploration = exploration
        self.relative_explo = relative_exploration
        self.log = _null_logger if logger is None else logger

    def _belief_start(self):
        if self._belief == 'array':
            return ArrayBelief(self.model.start, **self._belief_params)
        elif self._belief == 'particle':
            return ParticleBelief(self.model.sample_start, self.model.n_states,
                                  **self._belief_params)
        else:
            raise ValueError('Unknown belief type: ' + self._belief)

    def get_node(self, history):
        """Raises ValueError if node does not exist or history is invalid."""
        node = self.root
        last_belief = node.belief
        for i, h in enumerate(history):
            if isinstance(node, _SearchActionNode):  # h is an observation
                if h not in node.children:
                    node.children[h] = self._observation_node_for_belief(
                        last_belief.successor(self.model, history[i - 1], h))
                node = node.children[h]
            else:  # h is an action
                last_belief = node.belief
                node = node.safe_get_child(h)
        return node

    def random_action(self):
        return np.random.randint(self.model.n_actions)

    def rollout_from_node(self, node, horizon):
        if horizon.is_reached():
            return 0
        else:
            full_return = 0.
            for _ in range(self.rollout_it):
                state = node.belief.sample()
                full_return += self._one_rollout_from_node(state, horizon.copy())
            node.update(full_return / self.rollout_it)  # Only counts one visit
            return full_return

    def _one_rollout_from_node(self, state, horizon):
        gamma = 1.
        full_return = 0.
        while not horizon.is_reached():
            a = self.random_action()
            new_state, o, r = self.model.sample_transition(a, state)
            horizon.decrement(a, state, new_state, o)
            state = new_state
            full_return += gamma * r
            gamma *= self.model.discount
        return full_return

    def simulate_from_node(self, node, action=None):
        state = node.belief.sample()
        self._simulate_from_node(node, state, self.horizon_gen(), a=action)

    def _observation_node_for_belief(self, b):
        return _SearchObservationNode(b, self.model.n_actions, **self._node_params)

    def _simulate_from_node(self, node, state, horizon, a=None):
        if horizon.is_reached():
            return node.value
        else:
            if a is None:
                a = node.get_best_action(
                    exploration=self.exploration,
                    relative_exploration=self.relative_explo)
            child = node.safe_get_child(a)
            new_s, o, r = self.model.sample_transition(a, state)
            horizon.decrement(a, state, new_s, o)
            if o not in child.children:
                try:
                    # Create node with updated belief
                    child.children[o] = self._observation_node_for_belief(
                        node.belief.successor(self.model, a, o))
                    # Use rollout
                    partial_return = self.rollout_from_node(child.children[o],
                                                            horizon)
                except MaxSamplesReached:
                    self.log('Maximum number of samples reached, skipping.')
                    partial_return = 0.
                    # Note maybe use more relevant value, but, since the event
                    # is rare, it should not impact the result
            else:
                # Continue regular search
                partial_return = self._simulate_from_node(
                    child.children[o], new_s, horizon)
            full_return = r + self.model.discount * partial_return
            child.update(full_return)
            node.update(full_return)
            # TODO belief update (not needed for exact belief)
            return full_return

    def to_dict(self, as_policy=False):
        return self.root.to_dict(self.model, as_policy=as_policy)

    def map(self, fun, join_children=_children_to_dict):
        """
        :param join_children: function(result, children)
            returns result_with_children
        """
        return self.root._map(fun, join_children)

class _ObservationLookupSearchTree(_SearchTree):

    def __init__(self, model, horizon, exploration,
                 relative_exploration=False, belief='array', belief_params={},
                 node_params={}, logger=None):
        self._obs_nodes = {}  # used in super for root initialization
        if belief == 'particle':
            raise ValueError(
                '_ObservationLookupSearchTree does not support particle belief')
        super(_ObservationLookupSearchTree, self).__init__(
            model, horizon, exploration,
            relative_exploration=relative_exploration,
            belief=belief, belief_params={},
            node_params=node_params, logger=logger)

    def _observation_node_for_belief(self, b):
        # Returns node for given belief, creating one if none exists
        if b not in self._obs_nodes:
            self._obs_nodes[b] = _SearchObservationNode(
                b, self.model.n_actions, **self._node_params)
        return self._obs_nodes[b]

    # Here we need to keep track of visited children since the tree is no more
    # a tree...
    def to_dict(self, as_policy=False):
        return self.root.to_dict(self.model, as_policy=as_policy,
                                 exclude_visited=set())

class _ValueAverage(object):

    def __init__(self, alpha=0):
        self.n_simulations = 0
        self.total_value = 0.
        assert(0 <= alpha <= 1)
        self.alpha = alpha

    @property
    def value(self):
        return 0. if self.n_simulations == 0 \
                else self.total_value / self.n_simulations

    def update(self, value):
        self.total_value = ((self.total_value + value) * (1 - self.alpha)
                            + self.alpha * self.n_simulations * value)
        self.n_simulations += 1

class _SearchNode(object):

    def __init__(self, alpha=.001):
        self._avg = _ValueAverage(alpha=alpha)
        self.children = {}

    def __str__(self):
        return "[" + ", ".join(["{}: {}".format(i, self.children[i])
                                for i in self._children_keys()]) + "]"

    def _children_keys(self):
        return sorted(self.children.keys())

    @property
    def n_simulations(self):
        return self._avg.n_simulations

    @property
    def value(self):
        return self._avg.value

    def update(self, value):
        self._avg.update(value)

    def to_dict(self, model, as_policy=False, exclude_visited=None,
                recursive=True):
        return {"value": self.value,
                "visits": self.n_simulations,
                "node": None,
                }

    def _map(self, fun, join_children):
        result = fun(self)
        child_results = [c._map(fun, join_children)
                         for c in self._iterate_children()]
        return join_children(result, child_results)

    def _iterate_children(self):
        return self.children.values()

class _SearchObservationNode(_SearchNode):
    """
    Children indexed by action.
    """

    def __init__(self, belief, n_actions, alpha=.001):
        super(_SearchObservationNode, self).__init__(alpha=alpha)
        self.belief = belief
        self.children = [None for _ in range(n_actions)]
        self._children_alpha = alpha

    def children_dict(self, model):
        return {model.actions[a]: c
                for a, c in enumerate(self.children) if c is not None}

    def _children_keys(self):
        return [i for i, c in enumerate(self.children) if c is not None]

    def _not_init_children(self):
        return [i for i, c in enumerate(self.children)
                if c is None or c.n_simulations == 0]

    def augmented_values(self, exploration=0, relative=False):
        # Note: nans are returned for not initialized children
        if exploration > 0 and relative:
            vals = [child.value if child is not None else np.nan
                    for child in self.children]
            exploration *= np.nanmax(vals) - np.nanmin(vals)
        l_ns = np.log(self.n_simulations)
        return [child.value + exploration * np.sqrt(l_ns / child.n_simulations)
                if child is not None else np.nan
                for child in self.children]

    def get_best_action(self, exploration=0, relative_exploration=False):
        not_init = self._not_init_children()
        if len(not_init) == 0:
            assert(self.n_simulations > 0)  # explored if children explored
            # Augmented greedy (UCT)
            a = np.argmax([self.augmented_values(
                exploration=exploration, relative=relative_exploration)])
        else:
            # Chose an unexplored action
            a = np.random.choice(not_init)
        return a

    def safe_get_child(self, a):
        if self.children[a] is None:
            self.children[a] = _SearchActionNode(alpha=self._children_alpha)
        return self.children[a]

    def _iterate_children(self):
        return filter(lambda c: c is not None, self.children)

    def to_dict(self, model, as_policy=False, observed=None,
                exclude_visited=None, recursive=True):
        children = recursive
        if exclude_visited is not None:
            if self.belief in exclude_visited:
                children = False
            else:
                exclude_visited.add(self.belief)
        base = super(_SearchObservationNode, self).to_dict(
            model, as_policy=as_policy, exclude_visited=exclude_visited)
        base["belief"] = self.belief.to_list()
        if as_policy:
            a = self.get_best_action()
            grand_children = self.safe_get_child(a).children
            base.update({
                "action": model.actions[a],
                "observed": observed,
                "values": [v if not math.isnan(v) else None
                           for v in self.augmented_values()],  # For json
                "exploration_terms": [
                    np.sqrt(np.log(self.n_simulations) / child.n_simulations)
                    if ((child is not None) and child.n_simulations > 0)
                    else None
                    for child in self.children
                    ],
                "child_visits": [c.n_simulations if c is not None else 0
                                 for c in self.children],
                })
            if children:
                base.update({
                    "observations": [model.observations[o]
                                     for o in grand_children],
                    "children": [
                        grand_children[o].to_dict(
                            model, as_policy=as_policy, observed=i,
                            exclude_visited=exclude_visited)
                        for i, o in enumerate(grand_children)],
                    })
        else:
            if children:
                base.update({
                    "actions": [model.actions[i]
                                for i, c in enumerate(self.children)
                                if c is not None],
                    "children": [c.to_dict(model, as_policy=as_policy,
                                           exclude_visited=exclude_visited)
                                 for c in self.children if c is not None],
                    })
            else:
                base.update({'actions': [], 'children': []})

        return base

class _SearchActionNode(_SearchNode):
    """
    Children indexed by observation.
    """

    def to_dict(self, model, as_policy=False, exclude_visited=None,
                recursive=True):
        if as_policy:
            raise NotImplemented
        else:
            base = super(_SearchActionNode, self).to_dict(
                model, as_policy=as_policy, exclude_visited=exclude_visited)
            base.update({
                "observations": [model.observations[o]
                                 for o in self.children],
                "children": [self.children[o].to_dict(
                                model, as_policy=as_policy,
                                exclude_visited=exclude_visited)
                             for o in self.children],
                })
            return base

class POMCPPolicyRunner(object):
    """
    :param particles: number of particles for belief estimation
    :param horizon: length of simulation episodes
    :param iterations: number of simulation episodes to run
    :param exploration: UCT exploration parameter (c in [Silver2010])
    :param belief_values: group values for histories with same belief
    """

    def __init__(self, model, particles=20, iterations=100, horizon=100,
                 exploration=None, relative_exploration=False,
                 belief_values=False, belief='array', belief_params={},
                 logger=None):
        if logger is None:
            from logging import warning as logger
        if exploration is None:
            exploration = 1. if relative_exploration else 100
        tree_class = (_ObservationLookupSearchTree if belief_values
                      else _SearchTree)
        if isinstance(horizon, Horizon._Generator):
            horizon_generator = horizon
        elif isinstance(horizon, Integral):
            horizon_generator = NTransitionsHorizon.generator(model, n=horizon)
        else:
            raise ValueError('Invalid horizon: ' + str(horizon))
        self.tree = tree_class(model, horizon_generator, exploration,
                               relative_exploration=relative_exploration,
                               belief=belief, belief_params=belief_params,
                               logger=logger)
        self.iterations = iterations
        self._reset()

    @property
    def actions(self):
        return self.tree.model.actions

    @property
    def observations(self):
        return self.tree.model.observations

    def reset(self, belief=None):
        self._reset(belief=belief)

    # Note the reason for having both _reset and reset
    # is that in the async subclass the former needs the thread
    # to be initialized, and __init__ calls _reset before the that.
    def _reset(self, belief=None):
        if belief is not None:
            raise NotImplementedError
        self.history = []
        self._node = self.tree.root
        self._last_action = None

    @property
    def belief(self):
        return self._node.belief

    def get_action(self, iterations=None):
        # Note iterations must be greater than the number of actions
        # to guarantee that any action chosen as best_action is explored first
        if iterations is None:
            iterations = self.iterations
        for _ in range(iterations):
            self.tree.simulate_from_node(self._node)
        a = self._node.get_best_action()
        # No exploration during exploitation?
        self._last_action = a
        return self.actions[a]

    def step(self, observation):
        if self._last_action is None:
            raise ValueError('Unknown last action')
            # TODO rethink the design of the PolicyRunner class
        o = self.observations.index(observation)
        self.history.extend([self._last_action, o])
        # TODO: Make sure that node exists (and explores from previous)
        self._node = self.tree.get_node(self.history)

    def trajectory_trees_from_starts(self, qvalue=False):
        return {"graphs": [self.tree.to_dict(as_policy=not qvalue)]}

    def run_trajectory(self, logger=None):
        if logger is None:
            from logging import info as logger
        model = self.tree.model
        R = 0
        logger('New trajectory')
        self.reset()
        s = model.sample_start()
        belief_quotient = model._int_to_state().belief_quotient
        belief_preferences = model._int_to_state().belief_preferences
        while not model.is_final(s):
            a = self.get_action()
            ns, o, r = model.sample_transition(model.actions.index(a), s)
            self.step(model.observations[o])
            logger('{} -- {} --> {}, {}, {}'.format(
                model._int_to_state(s),
                a,
                model._int_to_state(ns),
                model.observations[o],
                r))
            logger('belief: {} | {:.2f}'.format(
                format_belief_array(belief_quotient(self.belief.array)),
                belief_preferences(self.belief.array)[0]))
            s = ns
            R += r
        logger("Total reward: %f" % R)

class AsyncPOMCPPolicyRunner(POMCPPolicyRunner):

    class _Thread(threading.Thread):
        """Continuously explores and enable another thread to execute
        some exploitation in between two explorations.
        """

        def __init__(self, tree):
            super(AsyncPOMCPPolicyRunner._Thread, self).__init__()
            self.tree = tree
            self._node = tree.root
            self._action = None
            self._done = False
            self._done_exploring = threading.Event()
            self._done_exploiting = threading.Event()
            self._done_exploiting.set()

        def stop(self):
            self._done = True
            self._done_exploiting.set()

        def execute(self, fun, *args, **kwargs):
            """Waits until current exploration is done and execute fun.
            """
            self._done_exploring.wait()
            self._done_exploiting.clear()
            result = fun(*args, **kwargs)
            self._done_exploiting.set()
            return result

        def set_node(self, node):
            self._node = node
            self._action = None

        def set_action(self, action):
            self._action = action

        def explore(self):
            self.tree.simulate_from_node(self._node, action=self._action)

        def run(self):
            while not self._done:
                self._done_exploring.clear()
                self.explore()
                self._done_exploring.set()
                self._done_exploiting.wait()

    def __init__(self, *args, **kwargs):
        super(AsyncPOMCPPolicyRunner, self).__init__(*args, **kwargs)
        self.thread = self._Thread(self.tree)
        self.thread.start()

    def step(self, observation):
        super(AsyncPOMCPPolicyRunner, self).step(observation)
        self.thread.execute(self.thread.set_node, self._node)

    def reset(self, belief=None):
        self._reset(belief=belief)
        self.thread.execute(self.thread.set_node, self._node)

    def get_action(self, iterations=None):
        # Start with minimal number of iterations to have explored all actions
        a = self.thread.execute(super(AsyncPOMCPPolicyRunner, self).get_action)
        self.thread.execute(self.thread.set_action, self._last_action)
        return a

    def stop(self):
        self.thread.stop()
        if self.thread.isAlive():
            self.thread.join()

    def execute(self, *args, **kwargs):
        self.thread.execute(*args, **kwargs)

    def __del__(self):
        self.stop()

def export_pomcp(policy, destination, belief_as_quotient=False):
    if belief_as_quotient:
        dic = {}
        FLAG = '####'
        model = policy.tree.model

        def to_dict(node):
            if isinstance(node, _SearchObservationNode):
                d = node.to_dict(model, as_policy=True, recursive=False)
                d['belief'] = list(model._int_to_state().belief_quotient(
                    np.array(d['belief'])))
                a_i = model.actions.index(d['action'])
                d['ACTION_IDX'] = sum([c is not None
                                       for c in node.children[:a_i]])
            else:
                d = {"observations": [model.observations[o]
                                      for o in node.children]}
                d[FLAG] = True
            return d

        def join_children(d, children):
            if d.get(FLAG, False):  # Action node
                d.pop(FLAG)
                d['children'] = children
            else:  # Observation node
                i = d.pop('ACTION_IDX')
                child = children[i]
                d['observations'] = child['observations']
                d['children'] = child['children']
                for c, o in zip(d['children'], d['observations']):
                    c['observed'] = model.observations.index(o)
            return d

        dic['graphs'] = [policy.tree.map(to_dict, join_children)]
        dic['states'] = model.htm_names
    else:
        dic = policy.trajectory_trees_from_starts()
        dic['states'] = model.states
    dic['actions'] = model.actions
    dic['exploration'] = policy.tree.exploration
    dic['relative_exploration'] = policy.tree.relative_explo

    with open(destination, 'w') as f:
        json.dump(dic, f, indent=2)


#
# Training Deep Neural Networks on Noisy Labels with Bootstrapping
# http://www-personal.umich.edu/~reedscot/bootstrap.pdf
#

import torch
from torch.nn import Module
import torch.nn.functional as F

class SoftBootstrappingLoss(Module):
    """
    ``Loss(t, p) = - (beta * t + (1 - beta) * p) * log(p)``

    Args:
        beta (float): bootstrap parameter. Default, 0.95
        reduce (bool): computes mean of the loss. Default, True.
        as_pseudo_label (bool): Stop gradient propagation for the term ``(1 - beta) * p``.
            Can be interpreted as pseudo-label.
    """
    def __init__(self, beta=0.95, reduce=True, as_pseudo_label=True):
        super(SoftBootstrappingLoss, self).__init__()
        self.beta = beta
        self.reduce = reduce
        self.as_pseudo_label = as_pseudo_label

    def forward(self, y_pred, y):
        # cross_entropy = - t * log(p)
        beta_xentropy = self.beta * F.cross_entropy(y_pred, y, reduction='none')

        y_pred_a = y_pred.detach() if self.as_pseudo_label else y_pred
        # second term = - (1 - beta) * p * log(p)
        bootstrap = - (1.0 - self.beta) * torch.sum(F.softmax(y_pred_a, dim=1) * F.log_softmax(y_pred, dim=1), dim=1)

        if self.reduce:
            return torch.mean(beta_xentropy + bootstrap)
        return beta_xentropy + bootstrap

class HardBootstrappingLoss(Module):
    """
    ``Loss(t, p) = - (beta * t + (1 - beta) * z) * log(p)``
    where ``z = argmax(p)``

    Args:
        beta (float): bootstrap parameter. Default, 0.95
        reduce (bool): computes mean of the loss. Default, True.

    """
    def __init__(self, beta=0.8, reduce=True):
        super(HardBootstrappingLoss, self).__init__()
        self.beta = beta
        self.reduce = reduce

    def forward(self, y_pred, y):
        # cross_entropy = - t * log(p)
        beta_xentropy = self.beta * F.cross_entropy(y_pred, y, reduction='none')

        # z = argmax(p)
        z = F.softmax(y_pred.detach(), dim=1).argmax(dim=1)
        z = z.view(-1, 1)
        bootstrap = F.log_softmax(y_pred, dim=1).gather(1, z).view(-1)
        # second term = (1 - beta) * z * log(p)
        bootstrap = - (1.0 - self.beta) * bootstrap

        if self.reduce:
            return torch.mean(beta_xentropy + bootstrap)
        return beta_xentropy + bootstrap


import datetime

from sqlalchemy.dialects.postgresql import JSON
from app import db
from utils import clean_value

class Ticker(db.Model):

    __tablename__ = 'tickers'

    id = db.Column(db.Integer, primary_key=True)
    symbol = db.Column(db.String)
    name = db.Column(db.String)
    last_sale = db.Column(db.Float)
    market_cap = db.Column(db.Float)
    tso = db.Column(db.Integer, nullable=True)
    ipo_year = db.Column(db.Integer, nullable=True)
    sector = db.Column(db.String, nullable=True)
    industry = db.Column(db.String, nullable=True)
    exchange = db.Column(db.String)

    status = db.Column(db.Integer)
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)

    def parse_data(self, exchange, data):
        if market in ['NASDAQ', 'NYSE', 'AMEX']:
            self.symbol = data[0]
            self.name = data[1]
            self.last_sale = clean_value(float, data[2])
            self.market_cap = clean_value(float, data[3])
            self.tso = clean_value(int, data[4])
            self.ipo_year = clean_value(int, data[5])
            self.sector = data[6]
            self.industry = data[7]
            self.exchange = exchange

    def as_json(self):
        return dict(
            symbol=self.symbol,
            exchange=self.exchange,
            type=self.type,
            is_daily_sync=self.is_daily_sync,
            is_realtime_sync=self.is_realtime_sync
        )

class HistoricalPrice(db.Model):

    __tablename__ = 'historical_prices'

    id = db.Column(db.Integer, primary_key=True)
    ticker_id = db.Column(db.Integer, db.ForeignKey('tickers.id'))
    trading_date = db.Column(db.Date)
    open = db.Column(db.Float)
    high = db.Column(db.Float)
    low = db.Column(db.Float)
    close = db.Column(db.Float)
    volume = db.Column(db.Float)
    adj_close = db.Column(db.Float, nullable=True)

    ticker = db.relationship("Ticker")

    status = db.Column(db.Integer)
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)

    def parse_data(self, ticker, data):
        import datetime
        self.ticker_id =  ticker.id
        self.trading_date =  datetime.datetime.strptime(data['Date'], '%Y-%m-%d')
        self.open = clean_value(float, data['Open'])
        self.high = clean_value(float, data['High'])
        self.low = clean_value(float, data['Low'])
        self.close = clean_value(float, data['Close'])
        self.volume = clean_value(float, data['Volume'])
        self.adj_close = clean_value(float, data['Adj Close'])

    def as_json(self):
        return dict(
            symbol=self.ticker.symbol,
            exchange=self.ticker.exchange,
            trading_date=self.date.isoformat(' '),
            open=self.open,
            high=self.high,
            low=self.low,
            close=self.close,
            volume=self.volume,
            adj_close=self.adj_close
        )

class TickPrice(db.Model):

    __tablename__ = 'tick_prices'

    id = db.Column(db.Integer, primary_key=True)
    ticker_id = db.Column(db.Integer, db.ForeignKey('tickers.id'))
    trading_time = db.Column(db.DateTime)
    avg_daily_volume = db.Column(db.Float)
    book_value = db.Column(db.Float)
    change = db.Column(db.Integer)
    dividend_per_share = db.Column(db.Float)
    dividend_yield = db.Column(db.Float)
    earnings_per_share = db.Column(db.Float)
    ebitda = db.Column(db.Float)
    fifty_day_moving_avg = db.Column(db.Float)
    fifty_two_week_high = db.Column(db.Float)
    fifty_two_week_low = db.Column(db.Float)
    market_cap = db.Column(db.Float)
    price = db.Column(db.Float)
    price_book_ratio = db.Column(db.Float)
    price_earnings_growth_ratio = db.Column(db.Float)
    price_earnings_ratio = db.Column(db.Float)
    price_sales_ratio = db.Column(db.Float)
    short_ratio = db.Column(db.Float)
    two_hundred_day_moving_avg = db.Column(db.Float)
    volume = db.Column(db.Float)

    ticker = db.relationship("Ticker")

    status = db.Column(db.Integer)
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)

    def parse_data(self, ticker, data):
        import datetime
        self.ticker_id =  ticker.id
        self.trading_time = datetime.datetime.now()
        self.avg_daily_volume = clean(float, data['avg_daily_volume'])
        self.book_value = clean(float, data['book_value'])
        self.change = clean(float, data['change'])
        self.dividend_per_share = clean(float, data['dividend_per_share'])
        self.dividend_yield = clean(float, data['dividend_yield'])
        self.earnings_per_share = clean(float, data['earnings_per_share'])
        self.ebitda = clean(float, data['ebitda'])
        self.fifty_day_moving_avg = clean(float, data['fifty_day_moving_avg'])
        self.fifty_two_week_high = clean(float, data['fifty_two_week_high'])
        self.fifty_two_week_low = clean(float, data['fifty_two_week_low'])
        self.market_cap = clean(float, data['market_cap'])
        self.price = clean(float, data['price'])
        self.price_book_ratio = clean(float, data['price_book_ratio'])
        self.price_earnings_growth_ratio = clean(float, data['price_earnings_growth_ratio'])
        self.price_earnings_ratio = clean(float, data['price_earnings_ratio'])
        self.price_sales_ratio = clean(float, data['price_sales_ratio'])
        self.short_ratio = clean(float, data['short_ratio'])
        self.two_hundred_day_moving_avg = clean(float, data['two_hundred_day_moving_avg'])
        self.volume = clean(float, data['volume'])

    def as_json(self):
       return dict(
        trading_time=self.trading_time.isoformat(' '),
        avg_daily_volume=self.avg_daily_volume,
        book_value=self.book_value,
        change=self.change,
        dividend_per_share=self.dividend_per_share,
        dividend_yield=self.dividend_yield,
        earnings_per_share=self.earnings_per_share,
        ebitda=self.ebitda,
        fifty_day_moving_avg=self.fifty_day_moving_avg,
        fifty_two_week_high=self.fifty_two_week_high,
        fifty_two_week_low=self.fifty_two_week_low,
        market_cap=self.market_cap,
        price=self.price,
        price_book_ratio=self.price_book_ratio,
        price_earnings_growth_ratio=self.price_earnings_growth_ratio,
        price_earnings_ratio=self.price_earnings_ratio,
        price_sales_ratio=self.price_sales_ratio,
        short_ratio=self.short_ratio,
        two_hundred_day_moving_avg=self.two_hundred_day_moving_avg,
        volume=self.volume
       )

class TickPriceRaw(db.Model):

    __tablename__ = 'tick_price_raw'

    id = db.Column(db.Integer, primary_key=True)
    ticker_id = db.Column(db.Integer, db.ForeignKey('tickers.id'))
    data = db.Column(JSON)

    status = db.Column(db.Integer)
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)

    ticker = db.relationship("Ticker")


import bpy
from math import *

def intersects(obj1, obj2):
    obj1_x = obj1.location.x
    obj1_y = obj1.location.y
    obj1_z = obj1.location.z

    obj2_x = obj2.location.x
    obj2_y = obj2.location.y
    obj2_z = obj2.location.z

    obj1_dim_x = obj1.dimensions.x
    obj1_dim_y = obj1.dimensions.y
    obj1_dim_z = obj1.dimensions.z

    obj2_dim_x = obj2.dimensions.x
    obj2_dim_y = obj2.dimensions.y
    obj2_dim_z = obj2.dimensions.z

    th = 0.05*(((obj1_dim_x+obj1_dim_y + obj1_dim_z)/3 + (obj2_dim_x+obj2_dim_y + obj2_dim_z)/3)/2)

    if (fabs(obj1_x - obj2_x)) >= ((obj1_dim_x+obj2_dim_x)/2 + th) or (fabs(obj1_y - obj2_y)) >= ((obj1_dim_y+obj2_dim_y)/2 + th) or (fabs(obj1_z - obj2_z)) >= ((obj1_dim_z+obj2_dim_z)/2 + th):
        return False
    else:
        return True

g = bpy.data.groups["Group.001"]
count = 0
num = 0
for i in g.objects:
    num+=1
    for j in g.objects:
        if i == j:
            continue
        count+=intersects(i, j)

print(count)
print(num)
print((count/(num)))