text: /* BEGIN LICENSE BLOCK
   * Version: CMPL 1.1
   *
   * The contents of this file are subject to the Cisco-style Mozilla Public
   * License Version 1.1 (the "License"); you may not use this file except
   * in compliance with the License.  You may obtain a copy of the License
   * at www.eclipse-clp.org/license.
   * 
   * Software distributed under the License is distributed on an "AS IS"
   * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
   * the License for the specific language governing rights and limitations
   * under the License. 
   * 
   * The Original Code is  The ECLiPSe Constraint Logic Programming System. 
   * The Initial Developer of the Original Code is  Cisco Systems, Inc. 
   * Portions created by the Initial Developer are
   * Copyright (C) 2006 Cisco Systems, Inc.  All Rights Reserved.
   * 
   * Contributor(s): Pascal Brisset
   * 
   * END LICENSE BLOCK */
  // $Id: ec2il.cc,v 1.1 2006/09/23 01:54:04 snovello Exp $
  
  #define EC2IL_CC
  #include <string.h>
  #include <ilsolver/ilcpath.h>
  #include "ec2il.h"
  #include "stdecil.h"
  #include "classes.h"
  #include "outof.h"
  
  long
  EC_long(EC_word t)
  {
    long e;
    int x = t.is_long(&e);
    Assert(x == EC_succeed, "EC_long");
    return e;
  }
  
  inline int
  EC_functor_size(EC_word table)
  {
    EC_functor f;
    int x = table.functor(&f);
    Assert(x == EC_succeed, "EC_functor_size");
    return f.arity();
  }
  
  IlcBool
  binary_functor(EC_word e, EC_functor *f)
  {
    return (e.functor(f) == EC_succeed  && f->arity() == 2);
  }
  
  IlcIntVar
  var_or_int(EC_word w)
  {
    IlcInt i;
    IlcIntVar v;
  
    if (w.is_long(&i) == EC_succeed) {
      return IlcIntVar(m, i, i);
    } else if (w.is_handle(intvar_method, (void**)&v) == EC_succeed) {
      return v;
    }
    // var or int expected in var_or_int()
    throw Ec2ilException();
  }
  
  IlcIntVarArray
  array_of_list(EC_word list)
  {
    // compute the length of the list
    int size=0;
    EC_word p = list;
    while (p.is_nil() != EC_succeed) {
      EC_word head, tail;
      if (p.is_list(head, tail) == EC_succeed) {
        size++;
      } else {
        // Bad argument in sum/1, must be a cons
        throw Ec2ilException();
      }
      p = tail;
    } // size computed
  
    IlcIntVarArray array(m, size);
    
    // Store the variables or integers from the list in the array
    p = list;
    int index;
    for(index = 0; index < size; index++) {
      EC_word head, tail;
      p.is_list(head, tail); // No checks (done in the previous loop)
      array[index] = var_or_int(head);
      p = tail;
    } // array initialized
  
    return array;
  }
  
  IlcIntArray
  int_array_of_list(EC_word list)
  {
    // compute the length of the list
    int size=0;
    EC_word p = list;
    while (p.is_nil() != EC_succeed) {
      EC_word head, tail;
      if (p.is_list(head, tail) == EC_succeed) {
        size++;
      } else {
        // Bad argument in sum/1, must be a cons
        throw Ec2ilException();
      }
      p = tail;
    } // size computed
  
    IlcIntArray array(m, size);
    
    // Store the integers from the list in the array
    p = list;
    int index;
    for(index = 0; index < size; index++) {
      EC_word head, tail;
      p.is_list(head, tail); // No checks (done in the previous loop)
      IlcInt n;
      if (head.is_long(&n) == EC_succeed) {
        array[index] = n;
      } else {
        throw IsLongException();
      }
      p = tail;
    } // array initialized
  
    return array;
  }
  
  IlcIntExp
  sum(EC_word E) // The argument is a list of variables or integer
  {
    return (IlcSum(array_of_list(EC_argument(E, 1))));
  }
  
  
  IlcIntExp
  ec2il_expr(EC_word e)
  {
    long i;
    IlcIntVar var;
    EC_functor f;
  
    if (e.functor(&f) == EC_succeed) {
      switch (f.arity()) {
      case 1: {
        if (strcmp(f.name(), "sum") == 0) { // sum(of a list)
  	return sum(e);
        }
        // Unknown unary expression
        throw Ec2ilException();
      }
      case 2: {
        if (EC_argument(e, 1).is_long(&i) == EC_succeed) {
  	IlcIntExp arg2 = ec2il_expr(EC_argument(e, 2));
  	
  	if (strcmp(f.name(), "+") == 0) { return (i + arg2); };
  	if (strcmp(f.name(), "-") == 0) { return (i - arg2); };
  	if (strcmp(f.name(), "*") == 0) { return (i * arg2); };
  	if (strcmp(f.name(), "/") == 0) { return (i / arg2); };
  
  	// Unknown binary expression
  	throw Ec2ilException();
        } else if (EC_argument(e, 2).is_long(&i) == EC_succeed) {
  	IlcIntExp arg1 = ec2il_expr(EC_argument(e, 1));
  
  	if (strcmp(f.name(), "+") == 0) { return (arg1 + i); };
  	if (strcmp(f.name(), "-") == 0) { return (arg1 - i); };
  	if (strcmp(f.name(), "*") == 0) { return (arg1 * i); };
  	if (strcmp(f.name(), "/") == 0) { return (arg1 / i); };
  
  	// Unknown binary expression
  	throw Ec2ilException();
        } else {
  	IlcIntExp arg1 = ec2il_expr(EC_argument(e, 1));
        	IlcIntExp arg2 = ec2il_expr(EC_argument(e, 2));
  	
  	if (strcmp(f.name(), "+") == 0) { return (arg1 + arg2); };
  	if (strcmp(f.name(), "-") == 0) { return (arg1 - arg2); };
  	if (strcmp(f.name(), "*") == 0) { return (arg1 * arg2); };
  	if (strcmp(f.name(), "/") == 0) { return (arg1 / arg2); };
  
  	// Unknown binary expression
  	throw Ec2ilException();
        }
      }
      default:
        // Unknown compound expression
        throw Ec2ilException();
      }
    } else /* non compound */ if (e.is_long(&i) == EC_succeed) { // Integer
      IlcIntExp e = (zero + i);
      return e;
    } else if (e.is_handle(intvar_method, (void**)&var) == EC_succeed) {// Variable
      return var;
    } else {
      // Unknown integer expression
      throw Ec2ilException();
    }
  }
  
  IlcConstraint
  distribute(EC_word c)
  {
    // distribute(Cards, Values, Vars, Level)
    // if Level = 0 then IlcBasic else IlcExtended
    
    EC_word Cards = EC_argument(c, 1);
    EC_word Values = EC_argument(c, 2);
    EC_word Vars = EC_argument(c, 3);
    int nb_values = EC_functor_size(Cards);
    int nb_vars = EC_functor_size(Vars);
    long level; EC_argument(c, 4).is_long(&level);
    if (nb_values != EC_functor_size(Values)) { throw Ec2ilException(); }
    
    IlcIntVarArray cards(m, nb_values);
    IlcIntArray values(m, nb_values);
    IlcIntVarArray vars(m, nb_vars);
    
    // Initialize cards and values from Cards and Values
    for(int i = 0; i < nb_values; i++) {
      cards[i] = var_or_int(EC_argument(Cards, i+1));
      
      IlcInt val;
      if (EC_argument(Values, i+1).is_long(&val) == EC_succeed) {
        values[i] = val;
      } else {
        // Integers expected in array Values
        throw Ec2ilException();
      }
    }
    // Initialize vars from Vars
    for(i = 0; i < nb_vars; i++) {
      vars[i] = var_or_int(EC_argument(Vars, i+1));
    }
    
    if (level == 0) {
      return IlcDistribute(cards, values, vars, IlcBasic);
    } else {
      return IlcDistribute(cards, values, vars, IlcExtended);
    }
  }
  
  
  IlcConstraint
  element(EC_word C) // element(Index, List, Value)
  {
    IlcIntExp arg1 = ec2il_expr(EC_argument(C,1));
    EC_word List = EC_argument(C, 2);
    IlcIntExp arg3 = ec2il_expr(EC_argument(C,3));
  
    // array[0] unused (because array[arg1-1] does not propagate correctly)
    List = list(EC_word(0L), List);
  
    try { // Try with an array of integers
      IlcIntArray array = int_array_of_list(List);
      return (arg1 > 0 && array[arg1] == arg3);
    }
    catch (IsLongException) { // Bad luck, the List contains variables
      IlcIntVarArray array = array_of_list(List);
      return (arg1 > 0 && array[arg1] == arg3);
    }
  }
  
  
  IlcConstraint
  all_diff(EC_word C, EC_functor f) // alldistinct(Table) or alldifferent(Table)
  {
    EC_word table = EC_argument(C, 1);
    int size = EC_functor_size(table);
    
    IlcIntVarArray array(m, size);
    for(int i = 0; i < size; i++) {
      array[i] = var_or_int(EC_argument(table, i+1));
    }
    
    if (strcmp(f.name(), "alldistinct") == 0) {
      return IlcAllDiff(array, IlcWhenValue);
    }
    if (strcmp(f.name(), "alldifferent") == 0) {
      return IlcAllDiff(array, IlcWhenDomain);
    }
  }
  
  class DistanceI : public IlcPathTransitI {
    IlcFloat *_distance;
    int _size;
  public:
    DistanceI(EC_word, int);
    IlcFloat transit(IlcInt i,IlcInt j) { return _distance[i*_size+j]; }
  };
  
  DistanceI::DistanceI(EC_word ArrayDistances, int size): _size(size)
  {
    _distance = new (m.getHeap()) (IlcFloat[size*size]);
    int i;
  
    for(i = 1; i <= size; i++) {
      EC_word From_i = EC_argument(ArrayDistances, i);
      int j;
      for(j = 1; j <= size; j++) {
        _distance[(i-1)*size+j-1] = IlcFloat(EC_long(EC_argument(From_i, j)));
      }
    }
  }
  
  IlcPathTransit Distance(IlcManager m, EC_word ArrayDistances, int size)
  {
    return new (m.getHeap()) DistanceI(ArrayDistances, size);
  }
  
  
  IlcConstraint
  path(EC_word Path) // path(NextCumulArray, ArrayDistances, MaxNbPaths, WhenEvent)
  {
   IlcInitFloat();
  
    EC_word NextCumulArray = EC_argument(Path, 1); // Array of couples
    EC_word ArrayDistances = EC_argument(Path, 2); // Matrix
    IlcInt maxNbPaths = EC_long(EC_argument(Path, 3));
    // if WhenEvent = 0 then WhenValue else When WhenDomain
    IlcWhenEvent whenEvent =
      (EC_long(EC_argument(Path, 4)) == 0 ? IlcWhenValue: IlcWhenDomain);
  
    IlcInt nbNodes = EC_functor_size(NextCumulArray);
  
    // Construction of next and cumul
    IlcIntVarArray next(m, nbNodes);
    IlcFloatVarArray cumul(m, nbNodes);
    int i;
    for(i = 1; i <= nbNodes; i++) {
      EC_word NextCumul = EC_argument(NextCumulArray, i);
      EC_word Next = EC_argument(NextCumul, 1);
      EC_word Cumul = EC_argument(NextCumul, 2);
  
      next[i-1] = var_or_int(Next);
      cumul[i-1] = IlcFloatVar(var_or_int(Cumul));
    } // next and cumul initialized
  
    // Construction of the transit function
    IlcPathTransit transit = Distance(m, ArrayDistances, nbNodes);
    return(IlcPath(next, cumul, transit, maxNbPaths, whenEvent));
  }
  
  IlcConstraint
  outof(EC_word Outof) // outof(Var, List), forall X in Array, Var ## X
  {
    IlcIntVar var = var_or_int(EC_argument(Outof, 1));
    EC_word List = EC_argument(Outof, 2);
  
    return OutOf(var, array_of_list(List));
  }
  
  
  
  IlcConstraint
  ec2il_cstr(EC_word c)
  {
    EC_functor f;
  
    if (c.functor(&f) == EC_succeed) {
      switch (f.arity()) {
      case 1: {
        if (strcmp(f.name(), "alldistinct") == 0 || strcmp(f.name(), "alldifferent") == 0) {
  	return all_diff(c, f);
        } else if (strcmp(f.name(), "#\\+") == 0) { // NEGATION
  	return (! ec2il_cstr(EC_argument(c, 1)));
        }
  	
        // Unknown unary constraint
        throw Ec2ilException();
      }
      case 2: {
        EC_word c1 = EC_argument(c,1);
        EC_word c2 = EC_argument(c,2);
  
        if (strcmp(f.name(), "#/\\") == 0 || strcmp(f.name(), "#\\/") == 0 ||
  	  strcmp(f.name(), "#=>") == 0 || strcmp(f.name(), "#<=>") == 0) {
  	IlcConstraint arg1 = ec2il_cstr(c1);
  	IlcConstraint arg2 = ec2il_cstr(c2);
  
  	if (strcmp(f.name(), "#/\\") == 0) { return (arg1 && arg2); };
  	if (strcmp(f.name(), "#\\/") == 0) { return (arg1 || arg2); };
  	if (strcmp(f.name(), "#=>") == 0) { return ((! arg1) || arg2); };
  	if (strcmp(f.name(), "#<=>") == 0) { return (arg1 == arg2); };
        } else if (strcmp(f.name(), "isd") == 0) {
  	IlcIntExp arg1 = ec2il_expr(c1);
  	IlcConstraint arg2 = ec2il_cstr(c2);
  
  	return (arg1 >= 0 && arg1 <= 1 && (arg1 == 1) == arg2);
        } else if (strcmp(f.name(), "outof") == 0) {
  	return outof(c);
        } else { // Binary constraint on expressions
  	IlcIntExp arg1 = ec2il_expr(c1);
  	IlcIntExp arg2 = ec2il_expr(c2);
  	
  	if (strcmp(f.name(), "#=") == 0) { return (arg1 == arg2); };
  	if (strcmp(f.name(), "##") == 0) { return (arg1 != arg2); };
  	if (strcmp(f.name(), "#\\=") == 0) { return (arg1 != arg2); };
  	if (strcmp(f.name(), "#>") == 0) { return (arg1 > arg2); };
  	if (strcmp(f.name(), "#<") == 0) { return (arg1 < arg2); };
  	if (strcmp(f.name(), "#>=") == 0) { return (arg1 >= arg2); };
  	if (strcmp(f.name(), "#<=") == 0) { return (arg1 <= arg2); };
  	if (strcmp(f.name(), "#>") == 0) { return (arg1 > arg2); };
  	
  	// Unknown binary constraint
  	throw Ec2ilException();
        }
      }
      case 3: {
        if (strcmp(f.name(), "element") == 0) { // ELEMENT constraint
  	return element(c);
        } 
  
        // Unknown ternary constraint
        throw Ec2ilException();
      }
      case 4: {
        if (strcmp(f.name(), "distribute") == 0) {
  	return distribute(c);
        } else if (strcmp(f.name(), "path") == 0) {
  	return path(c);
        }
         
        // Unknown 4-ary constraint
        throw Ec2ilException();
      }
      default: {
        // Unknown constraint
        throw Ec2ilException();
      }
      }
    } else { // Not a compound term
      throw Ec2ilException();
    }
  }
  
  