def __init__(self, settings):
     self.settings = settings
                    
     filename_beacons = self.settings.DATA_FILE_PATH + self.settings.BEACON_PLACEMENT_FILE
     
     # loading locations of BLE beacon devices        
     self.beacon_list = {}    
     file_beacons = open(filename_beacons, "r")
     next_beacon_id = 0
     for line in file_beacons.readlines():
         elements = line.strip().split(",")
         if int(elements[3]) == 1:                
             beacon_id = next_beacon_id
             next_beacon_id += 1
             x_pos = int(elements[1])
             y_pos = int(elements[2])
             self.beacon_list[beacon_id] = (x_pos, 24-y_pos)
         
     filename_walls = self.settings.DATA_FILE_PATH + "walls.csv"      
     
     # loading walls         
     self.list_walls = []    
     file_walls = open(filename_walls, "r")
     for line in file_walls.readlines():
         elems = line.strip().split(",")
         self.list_walls.append(((int(elems[1]), 24-int(elems[2])), (int(elems[3]), 24-int(elems[4]))))
         
     # loading an external Java class
     self.policy_calculator = PolicyCalculator()
class TrajectoryIdentification:
        
    def __init__(self, settings):
        self.settings = settings
                       
        filename_beacons = self.settings.DATA_FILE_PATH + self.settings.BEACON_PLACEMENT_FILE
        
        # loading locations of BLE beacon devices        
        self.beacon_list = {}    
        file_beacons = open(filename_beacons, "r")
        next_beacon_id = 0
        for line in file_beacons.readlines():
            elements = line.strip().split(",")
            if int(elements[3]) == 1:                
                beacon_id = next_beacon_id
                next_beacon_id += 1
                x_pos = int(elements[1])
                y_pos = int(elements[2])
                self.beacon_list[beacon_id] = (x_pos, 24-y_pos)
            
        filename_walls = self.settings.DATA_FILE_PATH + "walls.csv"      
        
        # loading walls         
        self.list_walls = []    
        file_walls = open(filename_walls, "r")
        for line in file_walls.readlines():
            elems = line.strip().split(",")
            self.list_walls.append(((int(elems[1]), 24-int(elems[2])), (int(elems[3]), 24-int(elems[4]))))
            
        # loading an external Java class
        self.policy_calculator = PolicyCalculator()
    
    # calculate the number of combinations (nCr)
    def ncr(self, n, r):
        r = min(r, n-r)
        if r == 0: return 1
        numer = reduce(op.mul, xrange(n, n-r, -1))
        denom = reduce(op.mul, xrange(1, r+1))
        return numer//denom   
             
    # radio reception model; should be replaced by more realistic one    
    def beaconReceptionProbability(self, distance):
        if distance < self.settings.RADIO_RANGE:        
            return self.settings.BEACON_RECEPTION_PROBABILITY
        else:
            return 0.001 # very small value; intending to avoid zero-division
        
    # check the presence of line-of-sight between a client and a beacon device
    def checkLOS(self, node_pos, neighbor_pos, walls):

        x1 = node_pos[0]
        y1 = node_pos[1]
        x2 = neighbor_pos[0]
        y2 = neighbor_pos[1]
        
        for wall in walls:
            x3 = wall[0][0]
            y3 = wall[0][1]
            x4 = wall[1][0]
            y4 = wall[1][1]
                        
            ta = (x3-x4)*(y1-y3)+(y3-y4)*(x3-x1)
            tb = (x3-x4)*(y2-y3)+(y3-y4)*(x3-x2)
            tc = (x1-x2)*(y3-y1)+(y1-y2)*(x1-x3)
            td = (x1-x2)*(y4-y1)+(y1-y2)*(x1-x4)

            if ta * tb < 0 and tc * td < 0:
                return False
        
        return True    
    
    # main function for trajectory identification                         
    def trajectory_identification(self, ground_truth_trajectory, consider_walls=True):
        
        # just some aliases
        TIME_STEP = self.settings.TIME_STEP
        SIMULATION_TIME = self.settings.SIMULATION_TIME
        WINDOW_SIZE = self.settings.WINDOW_SIZE
        
        output_filename = self.settings.OUTPUT_FILE_PATH + self.settings.OUTPUT_FILE_NAME        
        output_file = open(output_filename, "w")
                        
        beacon_list = self.beacon_list
        
        output_file.write("n_nodes=%d, consider_walls=%s, n_beacons=%d, beacon_reception_probability=%f, likelihood_threshold=%f, random_seed=%d, time_step=%d, window_size=%d, " % (self.settings.N_NODES, consider_walls, len(beacon_list), self.settings.BEACON_RECEPTION_PROBABILITY, self.settings.LIKELIHOOD_THRESHOLD, self.settings.RANDOM_SEED, self.settings.TIME_STEP, self.settings.WINDOW_SIZE))
        output_file.write("k=")
        for k in self.settings.PARAM_K:
            output_file.write(" %d" % k)
        output_file.write(", w_max=")
        for w_max in self.settings.MAX_SET_SIZE:
            output_file.write(" %d" % w_max)
        output_file.write("\n")
                       
        if consider_walls:
            list_walls = self.list_walls
            print list_walls
        else:
            list_walls = []            
            
        beacon_reception_probability = HashMap()
                
        for trajectory_id in ground_truth_trajectory.keys():
            beacon_reception_probability.put(trajectory_id, HashMap())
            for beacon_id in beacon_list.keys():
                beacon_reception_probability.get(trajectory_id).put(beacon_id, HashMap())
                
        # set of beacons that are received by each client
        received_beacons = {}
        for trajectory_id in ground_truth_trajectory.keys():
            received_beacons[trajectory_id] = set([])
        
        decryptability_table = {}           
        for current_time in range(0, SIMULATION_TIME, TIME_STEP):
                        
            param_id = (current_time/TIME_STEP) % len(self.settings.PARAM_K)
            k = self.settings.PARAM_K[param_id]
            max_size_w = self.settings.MAX_SET_SIZE[param_id]
            
            print "time=", current_time / (TIME_STEP * len(self.settings.PARAM_K)) * (TIME_STEP * len(self.settings.PARAM_K)), ", k=", k
                                                                    
            for trajectory_id in ground_truth_trajectory.keys():
                
                # simulating wireless communication between BLE beacons                
                for beacon_id in beacon_list.keys():
                    if self.checkLOS(ground_truth_trajectory[trajectory_id][current_time], beacon_list[beacon_id], list_walls):
                        distance = math.sqrt((ground_truth_trajectory[trajectory_id][current_time][0] - beacon_list[beacon_id][0])**2 + (ground_truth_trajectory[trajectory_id][current_time][1] - beacon_list[beacon_id][1])**2)
                        beacon_reception_probability.get(trajectory_id).get(beacon_id).put(current_time, self.beaconReceptionProbability(distance))
                        if random.random() < beacon_reception_probability.get(trajectory_id).get(beacon_id).get(current_time):
                            signature = Vector()
                            signature.add(beacon_id)
                            signature.add(current_time)
                            received_beacons[trajectory_id].add(signature)
                    else:
                        beacon_reception_probability.get(trajectory_id).get(beacon_id).put(current_time, 0.0)
                        
                    if current_time - TIME_STEP * len(self.settings.PARAM_K) * WINDOW_SIZE >= 0:
                        beacon_reception_probability.get(trajectory_id).get(beacon_id).remove(current_time - TIME_STEP * len(self.settings.PARAM_K) * WINDOW_SIZE)
                        
            # trajectory identification phase
            if current_time < WINDOW_SIZE * TIME_STEP * len(self.settings.PARAM_K):
                continue

            if current_time % (TIME_STEP * len(self.settings.PARAM_K)) == 0:
                decryptability_table[current_time] = {}                                             

            for target_trajectory_id in ground_truth_trajectory.keys():
                
                # decryptability_table[t][i][j] is True if the client i can decrypt trajectory j at time t
                if current_time % (TIME_STEP * len(self.settings.PARAM_K)) == 0:
                    decryptability_table[current_time][target_trajectory_id] = {}
                    for trajectory_id in ground_truth_trajectory.keys():
                        decryptability_table[current_time][target_trajectory_id][trajectory_id] = False
                
                # set of all the location signatures that the client has received during the recent ${WINDOW_SIZE} time steps                                            
                all_location_signatures = []
                for beacon_id in beacon_list.keys():
                    for time in range(current_time - (WINDOW_SIZE - 1) * (TIME_STEP * len(self.settings.PARAM_K)), current_time + 1, TIME_STEP * len(self.settings.PARAM_K)):
                        if beacon_reception_probability.get(target_trajectory_id).get(beacon_id).get(time) > self.settings.MIN_RECEPTION_PROBABILITY:
                            aposteriori_prob = beacon_reception_probability.get(target_trajectory_id).get(beacon_id).get(time) / sum([beacon_reception_probability.get(trajectory_id).get(beacon_id).get(time) for trajectory_id in ground_truth_trajectory.keys()])
                            signature = Vector()
                            signature.add(beacon_id)
                            signature.add(time)
                            all_location_signatures.append((signature, aposteriori_prob))
                            
                #
                # selecting the set W (this process is off-loaded to Java code) 
                #
                            
                all_location_signatures.sort(key=lambda a: a[1], reverse=1)
                all_location_signatures = [elem[0] for elem in all_location_signatures]
                                                    
                if len(all_location_signatures) < k:
                    print "trajectory_id=", target_trajectory_id, ", time=", current_time / (TIME_STEP * len(self.settings.PARAM_K)) * (TIME_STEP * len(self.settings.PARAM_K)), ", k=", k, ", W= N/A"
                    output_file.write("0,%d,%d,%d,N/A,N/A\n" % (target_trajectory_id, current_time / (TIME_STEP * len(self.settings.PARAM_K)) * (TIME_STEP * len(self.settings.PARAM_K)), k))                                    
                else:                                        
                    size_of_W = self.policy_calculator.calcPolicy(current_time, all_location_signatures, beacon_reception_probability, target_trajectory_id, self.settings.LIKELIHOOD_THRESHOLD, k, min(len(all_location_signatures), max_size_w))
                    
                    #
                    # Selection of the set W (Python version) (slow; it seems that overhead for reference of dictionary values significantly affects the total execution time)
                    #
                    
#                     size_of_W = max_size_w

#                     size_of_W = k
#                     for set_size in range(k, len(all_location_signatures)+1):
#                         sum_likelihood = 0.0
#                         target_likelihood = 0.0
#                         for trajectory_id in ground_truth_trajectory.keys():
#                             probability = 1.0
#                             for signature_idx in range(set_size-k, set_size):
#                                 probability *= beacon_reception_probability.get(trajectory_id).get(all_location_signatures[signature_idx][0]).get(all_location_signatures[signature_idx][1])
#                             if trajectory_id == target_trajectory_id:
#                                 target_likelihood = probability
#                             sum_likelihood += probability
#                         aposteriori_prob = target_likelihood / sum_likelihood
#                         
#                         if aposteriori_prob > self.settings.LIKELIHOOD_THRESHOLD:
#                             size_of_W = set_size
#                         else:
#                             break
                        

#                     for set_size in reversed(range(k, )):                        
                        
                        
#                         for k_set in itertools.combinations(range(set_size), k):
#                             if not aposteriori_prob.has_key(k_set):                              
#                                 likelihood = {}
#                                 for trajectory_id in ground_truth_trajectory.keys():
#                                     probability = 1.0
#                                     for signature_idx in k_set:
#                                         probability *= beacon_reception_probability.get(trajectory_id).get(all_location_signatures[signature_idx][0]).get(all_location_signatures[signature_idx][1])
#                                     likelihood[trajectory_id] = probability
#                                 aposteriori_prob[k_set] = likelihood[target_trajectory_id] / sum(likelihood.values())
# #                                 aposteriori_prob[k_set] = 0.9
# 
#                             if aposteriori_prob[k_set] < self.settings.LIKELIHOOD_THRESHOLD:
#                                 is_valid = False
#                                 break
                            
                    if size_of_W > 0:
                        print "trajectory_id=", target_trajectory_id, ", time=", current_time / (TIME_STEP * len(self.settings.PARAM_K)) * (TIME_STEP * len(self.settings.PARAM_K)), ", k=", k, ", |W|=", size_of_W
                        output_file.write("0,%d,%d,%d,%d," % (target_trajectory_id, current_time / (TIME_STEP * len(self.settings.PARAM_K)) * (TIME_STEP * len(self.settings.PARAM_K)), k, size_of_W))
                        output_file.write("\n")                                                                    
                        for trajectory_id in ground_truth_trajectory.keys():                            
                            if len(set(all_location_signatures[0:size_of_W]).intersection(received_beacons[trajectory_id])) >= k:
                                decryptability_table[current_time / (TIME_STEP * len(self.settings.PARAM_K)) * (TIME_STEP * len(self.settings.PARAM_K))][target_trajectory_id][trajectory_id] = True
                    else: 
                        print "trajectory_id=", target_trajectory_id, ", time=", current_time / (TIME_STEP * len(self.settings.PARAM_K)) * (TIME_STEP * len(self.settings.PARAM_K)), ", k=", k, ", W= N/A"
                        output_file.write("0,%d,%d,%d,N/A,N/A\n" % (target_trajectory_id, current_time / (TIME_STEP * len(self.settings.PARAM_K)) * (TIME_STEP * len(self.settings.PARAM_K)), k))
        
        for time in decryptability_table.keys():
                for target_trajectory_id in ground_truth_trajectory.keys():
                    for trajectory_id in ground_truth_trajectory.keys():
                        output_file.write("1,%d,%d,%d,%s\n" % (time, target_trajectory_id, trajectory_id, decryptability_table[time][target_trajectory_id][trajectory_id]))    
        
        
        # calculating performance statistics
        n_true_positives = 0
        n_false_positives = 0
        n_true_negatives = 0
        n_false_negatives = 0
        for time in decryptability_table.keys():
            for target_trajectory_id in ground_truth_trajectory.keys():
                for trajectory_id in ground_truth_trajectory.keys():
                    if target_trajectory_id == trajectory_id:
                        if decryptability_table[time][target_trajectory_id][trajectory_id]:
                            n_true_positives += 1
                        else:
                            n_false_negatives += 1
                    else:
                        if decryptability_table[time][target_trajectory_id][trajectory_id]:
                            n_false_positives += 1
                        else:
                            n_true_negatives += 1
                            
        print "accuracy=", float(n_true_positives + n_true_negatives) / (n_true_positives + n_false_positives + n_true_negatives + n_false_negatives)
        precision = float(n_true_positives) / (n_true_positives + n_false_positives)
        recall = float(n_true_positives) / (n_true_positives + n_false_negatives)
        f_measure = 2*precision*recall / (precision+recall)
        print n_true_positives, n_true_negatives, n_false_positives, n_false_negatives
        output_file.write("2,%f,%f,%f,%d,%d,%d,%d\n" % (precision, recall, f_measure, n_true_positives, n_true_negatives, n_false_positives, n_false_negatives))
        
        output_file.close()
            
                    
# selection of W by depth-first search; super slow :( 
                
#                 if len(all_location_signatures) < self.settings.PARAM_K:
#                     print "trajectory_id=", target_trajectory_id, ", time=", current_time, ", W= NaN"
#                 else:                                    
#                     valid_combinations = []
#                     for k_set in itertools.combinations(range(len(all_location_signatures)), self.settings.PARAM_K):                         
#                         likelihood = {}
#                         for trajectory_id in ground_truth_trajectory.keys():
#                             probability = 1.0
#                             for signature_idx in k_set:
#                                 probability *= beacon_reception_probability[trajectory_id][all_location_signatures[signature_idx][0]][all_location_signatures[signature_idx][1]]
#                             likelihood[trajectory_id] = probability
#                         aposteriori_prob = likelihood[target_trajectory_id] / sum(likelihood.values())                    
#                         if aposteriori_prob > 0.8:
#                             label = 0
#                             for i in range(len(all_location_signatures)):
#                                 if i in k_set:
#                                     label += pow(2, i)
#                             valid_combinations.append(label)                
#                     
#                     stack = ["1", "0"]
#                     visited = []
#                     best_label = ""
#                     while len(stack) > 0:
#                         label = stack.pop(0)
#                         if label not in visited:
#                             visited.append(label)                        
#                             if label.count("1") < self.settings.PARAM_K: 
#                                 if len(label) < len(all_location_signatures):
#                                     stack = [label + "1", label + "0"] + stack                    
#                             else:
#                                 num_valid_combinations = 0
#                                 target_label = sum([pow(2, i) for i in range(len(label)) if label[i] == "1"])
#                                 for valid_combination in valid_combinations:
#                                     if bin(valid_combination & target_label).count("1") == self.settings.PARAM_K:
#                                         num_valid_combinations += 1
#                                 if num_valid_combinations == self.ncr(label.count("1"), self.settings.PARAM_K):                        
#                                     if best_label.count("1") < label.count("1"):
#                                         best_label = label
#                                         if best_label.count("1") >= self.settings.MAX_SET_SIZE:
#                                             break
#                                         print best_label, best_label.count("1"), len(all_location_signatures), len(label)
#                                     if len(label) < len(all_location_signatures):
#                                         stack = [label + "1", label + "0"] + stack                     
#                     if best_label != "":
#                         print "trajectory_id=", target_trajectory_id, ", time=", current_time, ", W=", [all_location_signatures[i] for i in range(len(best_label)) if best_label[i] == "1"]
#                     else: 
#                         print "trajectory_id=", target_trajectory_id, ", time=", current_time, ", W= NaN"

        print 'Done.'