# Path: sas/operators.py
# class Operator(PartialState):
#   def __init__(self, args):
#     for k, v in args.items():
#         setattr(self, k, v)
#     self.args = args # TODO - use FrozenDict instead
#     self._frozen_args = frozenset(args.items())
#     self._hash = None
#     self.conditions = None
#     self.effects = None
#     self.test = lambda state: True
#   def eff(self):
#     return self.effects.items()
#   def apply(self, state):
#     return State(merge_dicts(state.values, self.effects))
#   def __call__(self, state):
#     if state not in self:
#       return None
#     return self.apply(state)
#   def __iter__(self):
#     yield self
#   def __len__(self):
#     return 1
#   def __eq__(self, other):
#     return (type(self) == type(other)) and (self._frozen_args == other._frozen_args)
#   def __ne__(self, other):
#     return not self == other
#   def __hash__(self):
#     if self._hash is None:
#       self._hash = hash((self.__class__, self._frozen_args))
#     return self._hash
#   def __str__(self):
#     return self.__class__.__name__ + str_object(self.args)
#   __repr__ = __str__
# 
# Path: sas/states.py
# class State(object):
#   def __init__(self, values):
#     self.values = {var: value for var, value in values.items() if value is not False}
#   def __getitem__(self, var):
#     if var not in self.values: return False
#     return self.values[var]
#   def __eq__(self, other):
#     return (type(self) == type(other)) and (self.values == other.values)
#   def __ne__(self, other):
#     return not self == other
#   def __hash__(self):
#     return hash((self.__class__, frozenset(self.values.items())))
#   def __str__(self):
#     return self.__class__.__name__ + str_object(self.values)
#   __repr__ = __str__
# 
# class Goal(PartialState): # TODO: unify PartialState and Goal
#   def __init__(self, values, test=lambda state: True):
#     self.conditions = values
#     self.test = test
#   def __eq__(self, other):
#     return (type(self) == type(other)) and (self.conditions == other.conditions)
#   def __ne__(self, other):
#     return not self == other
#   def __hash__(self):
#     return hash((self.__class__, frozenset(self.cond())))
#   def __str__(self):
#     return self.__class__.__name__ + str_object(self.conditions)
#   __repr__ = __str__
# 
# class PartialState(object):
#   def cond(self):
#     return self.conditions.items()
#   def __contains__(self, state):
#     return all(state[var] == value for var, value in self.cond()) #and self.test(state)
# 
# Path: sas/hsp.py
# def h_add(state, goal, operators):
#   _, operator_costs = compute_costs(state, goal, operators, op=sum)
#   return operator_costs[goal].cost if goal in operator_costs else None
# 
# def h_max(state, goal, operators):
#   _, operator_costs = compute_costs(state, goal, operators, op=max)
#   return operator_costs[goal].cost if goal in operator_costs else None


import random
from .operators import Operator
from .states import State, Goal, PartialState
from heapq import heappop, heappush
from collections import deque
from .hsp import h_add, h_max
#def effects(action):
#  if isinstance(action, Operator):
#    return action.effects
#  if isinstance(action, State):
#    return action.values # TODO - should include default false values
#  if isinstance(action, Goal):
#    return {}
#  raise ValueError(action)

def achieves(operator, item):
  (var, val) = item
  if isinstance(operator, Operator):
    return var in operator.effects and operator.effects[var] == val
  if isinstance(operator, UniqueOperator):
    return var in operator.action.effects and operator.action.effects[var] == val
  if isinstance(operator, State):
    return operator[var] == val
  if isinstance(operator, Goal):
    return False
  raise ValueError(operator)
  #return var in effects(action) and effects(action)[var] == val

def deletes(operator, item):
  (var, val) = item
  if isinstance(operator, Operator):
    return var in operator.effects and operator.effects[var] != val
  if isinstance(operator, UniqueOperator):
    return var in operator.action.effects and operator.action.effects[var] != val
  if isinstance(operator, State):
    return operator[var] != val

# Path: sas/operators.py
# class Operator(PartialState):
#   def __init__(self, args):
#     for k, v in args.items():
#         setattr(self, k, v)
#     self.args = args # TODO - use FrozenDict instead
#     self._frozen_args = frozenset(args.items())
#     self._hash = None
#     self.conditions = None
#     self.effects = None
#     self.test = lambda state: True
#   def eff(self):
#     return self.effects.items()
#   def apply(self, state):
#     return State(merge_dicts(state.values, self.effects))
#   def __call__(self, state):
#     if state not in self:
#       return None
#     return self.apply(state)
#   def __iter__(self):
#     yield self
#   def __len__(self):
#     return 1
#   def __eq__(self, other):
#     return (type(self) == type(other)) and (self._frozen_args == other._frozen_args)
#   def __ne__(self, other):
#     return not self == other
#   def __hash__(self):
#     if self._hash is None:
#       self._hash = hash((self.__class__, self._frozen_args))
#     return self._hash
#   def __str__(self):
#     return self.__class__.__name__ + str_object(self.args)
#   __repr__ = __str__
# 
# Path: sas/states.py
# class State(object):
#   def __init__(self, values):
#     self.values = {var: value for var, value in values.items() if value is not False}
#   def __getitem__(self, var):
#     if var not in self.values: return False
#     return self.values[var]
#   def __eq__(self, other):
#     return (type(self) == type(other)) and (self.values == other.values)
#   def __ne__(self, other):
#     return not self == other
#   def __hash__(self):
#     return hash((self.__class__, frozenset(self.values.items())))
#   def __str__(self):
#     return self.__class__.__name__ + str_object(self.values)
#   __repr__ = __str__
# 
# class Goal(PartialState): # TODO: unify PartialState and Goal
#   def __init__(self, values, test=lambda state: True):
#     self.conditions = values
#     self.test = test
#   def __eq__(self, other):
#     return (type(self) == type(other)) and (self.conditions == other.conditions)
#   def __ne__(self, other):
#     return not self == other
#   def __hash__(self):
#     return hash((self.__class__, frozenset(self.cond())))
#   def __str__(self):
#     return self.__class__.__name__ + str_object(self.conditions)
#   __repr__ = __str__
# 
# class PartialState(object):
#   def cond(self):
#     return self.conditions.items()
#   def __contains__(self, state):
#     return all(state[var] == value for var, value in self.cond()) #and self.test(state)
# 
# Path: sas/hsp.py
# def h_add(state, goal, operators):
#   _, operator_costs = compute_costs(state, goal, operators, op=sum)
#   return operator_costs[goal].cost if goal in operator_costs else None
# 
# def h_max(state, goal, operators):
#   _, operator_costs = compute_costs(state, goal, operators, op=max)
#   return operator_costs[goal].cost if goal in operator_costs else None


# Path: sas/pop.py
import random
from .operators import Operator
from .states import State, Goal, PartialState
from heapq import heappop, heappush
from collections import deque
from .hsp import h_add, h_max

#def effects(action):
#  if isinstance(action, Operator):
#    return action.effects
#  if isinstance(action, State):
#    return action.values # TODO - should include default false values
#  if isinstance(action, Goal):
#    return {}
#  raise ValueError(action)

def achieves(operator, item):
  (var, val) = item
  if isinstance(operator, Operator):
    return var in operator.effects and operator.effects[var] == val
  if isinstance(operator, UniqueOperator):
    return var in operator.action.effects and operator.action.effects[var] == val
  if isinstance(operator, State):
    return operator[var] == val
  if isinstance(operator, Goal):
    return False
  raise ValueError(operator)
  #return var in effects(action) and effects(action)[var] == val

def deletes(operator, item):
  (var, val) = item
  if isinstance(operator, Operator):
    return var in operator.effects and operator.effects[var] != val
  if isinstance(operator, UniqueOperator):
    return var in operator.action.effects and operator.action.effects[var] != val
  if isinstance(operator, State):
    return operator[var] != val

  if isinstance(operator, Goal):