old_contents: """
  This file contains the code for the interactive online datalog tutorial.
  https://sites.google.com/site/pydatalog/Online-datalog-tutorial
  """
  
  from pyDatalog import pyDatalog
  pyDatalog.create_atoms('parent,bill,ancestor,descendents,manager, X,Y,Z,N,F,  factorial,odd,even, _split')
  
  + parent(bill,'John Adams')
  
  print(parent(bill,X)) # prints [('John Adams',)]
  
  # specify what an ancestor is
  ancestor(X,Y) <= parent(X,Y)
  ancestor(X,Y) <= parent(X,Z) & ancestor(Z,Y)
  
  print(ancestor(bill, X)) # prints [('John Adams',)]
  
  # the manager of bill is John Adams. Bill has only one manager.
  + (manager[bill]=='John Adams') 
  
  print(manager[bill]==X) # prints [('John Adams',)]
  
  # use expressions and recursion to evaluate factorials
  (factorial[N] == F) <= (N < 2) & (F==1)
  (factorial[N] == F) <= (N > 1) & (F == N*factorial[N-1])
  
  print(factorial[3]==N) # prints [(6,)]
  
  # aggregate function
  # calculate the list of descendents, sorted by their name, and separated by ','
  (descendents[X]==concat(Y, order_by=Y, sep=',')) <= ancestor(X,Y) 
  
  print(descendents[bill]==X) # prints [('John Adams',)]
  
  # specify how a string can be split, reversibly
  _split(X, Y,Z) <= (X == Y+'-'+Z)
  _split(X, Y,Z) <= (Y == (lambda X: X.split('-')[0])) & (Z == (lambda X: X.split('-')[1]))
  
  # prints a set with one tuple : ('a-b', 'a', 'b')
  print(_split("a-b",X,Y))
  
  # unlimited depth of recursion
  + even(0)
  even(N) <= (N > 0) & odd(N-1)
  odd(N) <= (N > 0) & even(N-1)
  
  # prints a set with one element: the ('2000',) tuple
  print(even(20)) # prints [()], one result with 0 variable
new_contents: """
  This file contains the code for the interactive online datalog tutorial.
  https://sites.google.com/site/pydatalog/Online-datalog-tutorial
  """
  
  from pyDatalog import pyDatalog
  pyDatalog.create_atoms('parent,bill,ancestor,descendents,manager, X,Y,Z,N,N1,F,  factorial,odd,even, _split')
  
  + parent(bill,'John Adams')
  
  print(parent(bill,X)) # prints [('John Adams',)]
  
  # specify what an ancestor is
  ancestor(X,Y) <= parent(X,Y)
  ancestor(X,Y) <= parent(X,Z) & ancestor(Z,Y)
  
  print(ancestor(bill, X)) # prints [('John Adams',)]
  
  # the manager of bill is John Adams. Bill has only one manager.
  + (manager[bill]=='John Adams') 
  
  print(manager[bill]==X) # prints [('John Adams',)]
  
  # use expressions and recursion to evaluate factorials
  (factorial[N] == F) <= (N < 1) & (F==-factorial[-N])
  + (factorial[1]==1)
  (factorial[N] == F) <= (N > 1) & (F == N*factorial[N-1])
  
  print(factorial[3]==F) # prints [(6,)]
  
  # aggregate function
  # calculate the list of descendents, sorted by their name, and separated by ','
  (descendents[X]==concat(Y, order_by=Y, sep=',')) <= ancestor(X,Y) 
  
  print(descendents[bill]==X) # prints [('John Adams',)]
  
  # specify how a string can be split, reversibly
  _split(X, Y,Z) <= (X == Y+'-'+Z)
  _split(X, Y,Z) <= (Y == (lambda X: X.split('-')[0])) & (Z == (lambda X: X.split('-')[1]))
  
  print(_split("a-b",X,Y)) # prints [('a', 'b')]
  
  # unlimited depth of recursion
  + even(0)
  even(N) <= (N > 0) & odd(N-1)
  odd(N) <= (N > 0) & even(N-1)
  
  print(even(20)) # prints [()], i.e. one valid result with 0 variable
  
  
  ############################################ python in Datalog
  
  class Employee(pyDatalog.Mixin): # --> Employee inherits the pyDatalog capability to use logic clauses
      
      def __init__(self, name, manager, salary): # method to initialize Employee instances
          super(Employee, self).__init__() # calls the initialization method of the Mixin class
          self.name = name
          self.manager = manager           # direct manager of the employee, or None
          self.salary = salary             # monthly salary of the employee
      
      def __repr__(self): # specifies how to display an Employee
          return self.name
  
  John = Employee('John', None, 6800)
  Mary = Employee('Mary', John, 6300)
  Sam = Employee('Sam', Mary, 5900)
  
  pyDatalog.create_atoms('has_car, X')
  + has_car(Mary)
  print(has_car(X)) # prints [(Mary,)]
  
  print(Employee.salary[X]==6300) # prints [(Mary,)]
  print(X) # prints [Mary]
  print(X.v()) # 'v()' is a convenience function to get the first value of X (or None)
  
  print((Employee.salary[X]==6300) >= X) # prints the first value of X in the query, in one line
  
  # the salary class N of employee X is computed as a function of his/her salary
  # this statement is a logic equality, not an assignment !
  Employee.salary_class[X] = Employee.salary[X]//1000
  
  
  # What is the salary class of John ?
  print(John.salary_class) # prints 6
  
  # calculated attribute
  Mary.salary_class = ((Employee.salary_class[Mary]==X) >= X) 
  
  # all the indirect managers Y of employee X are derived from his manager, recursively
  Employee.indirect_manager(X,Y) <= (Employee.manager[X]==Y) & (Y != None)
  Employee.indirect_manager(X,Y) <= (Employee.manager[X]==Z) & Employee.indirect_manager(Z,Y) & (Y != None)
  
  # Who are the employees of John with a salary class of 5 ?
  result = (Employee.salary_class[X] == 5) & Employee.indirect_manager(X, John)
  print(result) # prints [(Sam,)]
  
  E = Employee # defines an alias for Employee
  Employee.salary_class[X] = E.salary[X]//1000
subject: Update examples from the tutorial