home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 02 Useful Techniques / 08 Zarozinski / src / ffllapi / FuzzyModelBase.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-12-05  |  59.0 KB  |  2,389 lines

  1. //
  2. // File:    FuzzyModelBase.cpp
  3. //
  4. // Purpose:    Implementation of the FuzzyModelBase class.  This class represents
  5. //            the rules associated with a set of variables
  6. //
  7. // Copyright ⌐ 1999-2001 Louder Than A Bomb! Software
  8. //
  9. // This file is part of the FFLL (Free Fuzzy Logic Library) project (http://ffll.sourceforge.net)
  10. // It is released under the BSD license, see http://ffll.sourceforge.net/license.txt for the full text.
  11. //
  12.  
  13. #include "FuzzyModelBase.h"
  14. #include "FuzzySetBase.h"
  15. #include "FuzzyOutVariable.h"
  16. #include "RuleArray.h"
  17. #include "DefuzzVarObj.h"
  18.  
  19. #include <fstream>
  20. #include <time.h>
  21. #include <math.h>
  22.  
  23. #ifdef _DEBUG  
  24. #undef THIS_FILE
  25. static char THIS_FILE[]=__FILE__;
  26. #endif
  27.  
  28. //
  29. // Function:    ~FuzzyModelBase()
  30. // 
  31. // Purpose:        Destructor for FuzzyModelBase
  32. //
  33. // Arguments:
  34. //
  35. //                none
  36. //
  37. // Returns:
  38. //
  39. //                nothing
  40. //
  41. // Author:    Michael Zarozinski
  42. // Date:    5/00
  43. // 
  44. // Modification History
  45. // Author    Date        Modification
  46. // ------    ----        ------------
  47. //
  48. //
  49. FuzzyModelBase::~FuzzyModelBase()
  50. {
  51.     // remove variables and perform any clean up
  52.     delete_vars();
  53.  
  54.     if (rules)
  55.         {
  56.         delete rules;
  57.         rules = NULL;
  58.         }
  59.  
  60. }; // end FuzzyModelBase::~FuzzyModelBase()
  61.  
  62.  
  63.  
  64. //
  65. // Function:    FuzzyModelBase()
  66. // 
  67. // Purpose:        Default constructor for FuzzyModelBase class
  68. //
  69. // Arguments:
  70. //
  71. //            none
  72. //
  73. // Returns:
  74. //
  75. //            nothing
  76. //
  77. // Author:    Michael Zarozinski
  78. // Date:    5/00
  79. // 
  80. // Modification History
  81. // Author    Date        Modification
  82. // ------    ----        ------------
  83. //
  84. //
  85. FuzzyModelBase::FuzzyModelBase() : FFLLBase(NULL)
  86. {
  87.     input_var_count = 0; 
  88.  
  89.       rules = NULL;  
  90.     input_var_arr = NULL;
  91.     output_var = NULL;
  92.  
  93.     model_name = ""; // clear out file name
  94.  
  95.     // default to MIN inference
  96.      inference_method = INFERENCE_OPERATION::MIN;
  97.  
  98. } // end FuzzyModelBase::FuzzyModelBase()
  99.  
  100.  
  101. //
  102. // Function:    delete_vars()
  103. // 
  104. // Purpose:        Delete all the variables for the model and clean
  105. //                up the memory they use.
  106. //
  107. // Arguments:
  108. //
  109. //            none
  110. //
  111. // Returns:
  112. //
  113. //            void
  114. //
  115. // Author:    Michael Zarozinski
  116. // Date:    5/00
  117. //    
  118. // Modification History
  119. // Author    Date        Modification
  120. // ------    ----        ------------
  121. //
  122. //
  123. void FuzzyModelBase::delete_vars()
  124. {
  125.  
  126.     // if we have input variables, delete them
  127.     if (input_var_arr)
  128.         {
  129.         for (int i = 0; i < input_var_count; i++)
  130.             {
  131.             if (input_var_arr[i])
  132.                 {
  133.                 delete input_var_arr[i];
  134.                 input_var_arr[i] = NULL;
  135.                 }
  136.             } // end loop through input vars
  137.         delete[] input_var_arr;
  138.         input_var_arr = NULL;
  139.         }
  140.  
  141.     // if we have an output variable, delete it
  142.     if (output_var)
  143.         {
  144.         delete output_var;
  145.         output_var = NULL;
  146.         }
  147.  
  148. } // end FuzzyModelBase::delete_vars()
  149.  
  150. //
  151. // Function:    add_input_variable()
  152. // 
  153. // Purpose:        Create and add an input variable to this model. This automatically
  154. //                renames the identifier if it conflicts with an existing one.
  155. //
  156. // Arguments:
  157. //
  158. //            const wchar_t*    _name                -    name of the variable we're adding. Note this can be NULL
  159. //                                                    in which case it'll be named "Variable X" AND the name passed
  160. //                                                    in may not be the final name because variable names MUST be
  161. //                                                    unique within the model
  162. //            RealType        start_x                -    Starting (left most) 'x' value for the variable
  163. //            RealType        end_x                -    Ending (right most) 'x' value for the variable
  164. //            bool            create_unique_id    -    if true, alter the identifier (if needed) to make it unique) default = true
  165. //
  166. // Returns:
  167. //
  168. //        0 if success
  169. //        non-zero if failure
  170. //
  171. // Author:    Michael Zarozinski
  172. // Date:    5/00
  173. // 
  174. // Modification History
  175. // Author    Date        Modification
  176. // ------    ----        ------------
  177. //
  178. //
  179. int FuzzyModelBase::add_input_variable(const wchar_t* _name, RealType start_x, RealType end_x, bool create_unique_id /* = true */)
  180.      FuzzyVariableBase*    var = new_variable();
  181.    
  182.     if (var->init(_name, start_x, end_x, create_unique_id))
  183.         {
  184.         delete var;
  185.         return -1;
  186.         }
  187.  
  188.     add_input_var_to_list(var);
  189.  
  190.     return 0;
  191.  
  192. } // end FuzzyModelBase::add_input_variable()
  193.  
  194. //
  195. // Function:    add_output_variable()
  196. // 
  197. // Purpose:        Create and add an output variable to this model. This automatically
  198. //                renames the identifier if it conflicts with an existing one.
  199. //
  200. // Arguments:
  201. //
  202. //            const wchar_t*    _name                -    name of the variable we're adding. Note this can be NULL
  203. //                                                    in which case it'll be named "Variable X" AND the name passed
  204. //                                                    in may not be the final name because variable names MUST be
  205. //                                                    unique within the model
  206. //            RealType        start_x                -    Starting (left most) 'x' value for the variable
  207. //            RealType        end_x                -    Ending (right most) 'x' value for the variable
  208. //            bool            create_unique_id    -    if true, alter the identifier (if needed) to make it unique) default = true
  209. //
  210. // Returns:
  211. //
  212. //        0 if success
  213. //        non-zero if failure
  214. //
  215. // Author:    Michael Zarozinski
  216. // Date:    5/00
  217. // 
  218. // Modification History
  219. // Author    Date        Modification
  220. // ------    ----        ------------
  221. //
  222. //
  223. int FuzzyModelBase::add_output_variable(const wchar_t* _name, RealType start_x, RealType end_x, bool create_unique_id /* = true */)
  224. {
  225.     // return error if we already have an output variable
  226.     if (output_var)
  227.         {
  228.         set_msg_text(ERR_OUT_VAR_EXISTS);
  229.         return -1;
  230.         }
  231.   
  232.      output_var = new_output_variable();
  233.  
  234.       if (output_var->init(_name, start_x, end_x, create_unique_id))
  235.         {
  236.         delete output_var;
  237.         output_var = NULL;
  238.         return -1;
  239.         }
  240.   
  241.     calc_rule_index_wrapper();
  242.  
  243.     return 0;
  244.  
  245. } // end FuzzyModelBase::add_output_variable()
  246.     
  247.  
  248. //
  249. // Function:    delete_variable()
  250. // 
  251. // Purpose:        This functions removes a single variable
  252. //                from the model and adjusts memory and rules appropriately.
  253. //                This is very different (despite the naming) from delete_vars().
  254. //                That is called during the destruction of the model, this is used
  255. //                to remove a specific variable from the model.
  256. //
  257. // Arguments:
  258. //
  259. //        int _var_idx - index of the variable to remove
  260. //
  261. // Returns:
  262. //
  263. //        0 on success
  264. //        non-zero on failure
  265. //
  266. // Author:    Michael Zarozinski
  267. // Date:    5/00
  268. // 
  269. // Modification History
  270. // Author    Date        Modification
  271. // ------    ----        ------------
  272. //
  273. //    
  274. int FuzzyModelBase::delete_variable(int _var_idx )
  275. {
  276.     int        i, j;            // counter
  277.     int        new_mem_size;    // size of new rules array
  278.  
  279.     // don't allow del of output var...
  280.  
  281.     FuzzyVariableBase*    var= get_var(_var_idx);    // var we're dealing with
  282.  
  283.     // don't allow removal of output variable
  284.     if (var->is_output())
  285.         {
  286.         set_msg_text(ERR_CANT_DEL_OUTPUT_VAR);
  287.         return -1;
  288.         }
  289.  
  290.     int new_var_count = input_var_count - 1;
  291.      // need to create shrunken memory
  292.     FuzzyVariableBase** tmp_var;
  293.     
  294.     if (new_var_count)
  295.         tmp_var = new FuzzyVariableBase*[new_var_count];  
  296.     else
  297.         tmp_var = NULL; // no vars!
  298.  
  299.     if (!tmp_var && new_var_count)
  300.         {
  301.         // problem allocating memory
  302.         // NOTE: in MSVC new returns NULL if there's not enough memory. If this is ported
  303.         // to a diff platform new may throw a std::bad_alloc exception it it can't alloc the memory.
  304.          set_msg_text(ERR_ALLOC_MEM);
  305.         assert(tmp_var != NULL);
  306.         return -1;
  307.  
  308.         } // end if error allocating memory
  309.  
  310.     // copy the old memory to the new mem..
  311.     j = 0;
  312.     for (i = 0; i < input_var_count; i++)
  313.         {
  314.         if (i == _var_idx)
  315.             {
  316.             // this is the var we want to delete...
  317.             delete input_var_arr[i];
  318.             input_var_arr[i] = NULL;
  319.             continue;     
  320.             }
  321.  
  322.         tmp_var[j] = input_var_arr[i];
  323.         // reset the index of each var...
  324.         tmp_var[j]->set_index(j);
  325.         j++;
  326.  
  327.         } // end loop through input variables
  328.  
  329.     input_var_count--;  // increment the number of variables we have
  330.     
  331.     // set the old mem to the new mem...
  332.  
  333.     delete[] input_var_arr; // free old mem
  334.  
  335.     input_var_arr = tmp_var;
  336.  
  337.     // calculate how big the rules array needs to be...
  338.     new_mem_size = calc_num_of_rules();
  339.  
  340.     if (new_mem_size)
  341.         {
  342.         // this will free mem, re-allocate it, and clear it cuz
  343.         // deleting a var deletes all rules...
  344.         rules->alloc(new_mem_size);
  345.  
  346.         // re-calc at the variable index factors...
  347.         calc_rule_index_wrapper();
  348.         }
  349.  
  350.     return 0;
  351.  
  352. } // end FuzzyModelBase::delete_variable()
  353.  
  354. //
  355. // Function:    calc_rule_components(
  356. // 
  357. // Purpose:        Determines which sets comprise a rule.
  358. //
  359. // Arguments:
  360. //
  361. //        int        rule_index        - index of the rule to get the components for
  362. //        int*    set_idx_array    -        Array with N elements where N = the number of input variables.
  363. //                                        Each element corresponds to an input variable and the set in that
  364. //                                        variable that is involved in this rule is stored in that element.
  365. //
  366. // Returns:
  367. //
  368. //        void
  369. //
  370. // Author:    Michael Zarozinski
  371. // Date:    5/00
  372. // 
  373. // Modification History
  374. // Author    Date        Modification
  375. // ------    ----        ------------
  376. //
  377. //
  378. void FuzzyModelBase::calc_rule_components(int rule_index, int* set_idx_array) const
  379. {
  380.      int        i;        // counter
  381.     int        accum = 0;    // accumulator 
  382.     int last_in_var_idx = input_var_count - 1; // special value to let us know we're at the LAST array element
  383.  
  384.     // deal with special cases (first and last var) up front for speed considerations...
  385.     set_idx_array[0] = rule_index/get_rule_index(0);
  386.     set_idx_array[last_in_var_idx] = rule_index % input_var_arr[last_in_var_idx]->get_num_of_sets();
  387.  
  388.      // loop through the rest of the vars...
  389.  
  390.     // below is a slightly faster (or at least fewer assemby instruction) version of:
  391.     //
  392.     //    for (i = 1; i < last_in_var_idx; i++)
  393.     //        {
  394.     //        accum += set_idx_array[i-1] * get_rule_index(i-1);
  395.     //        set_idx_array[i] = (rule_index - accum)/get_rule_index(i);
  396.     //        }  
  397.     // I'll leave that version of the algorithm to help people figure out what I'm doing below
  398.  
  399.     i = 0;
  400.     while (i < last_in_var_idx )
  401.         {
  402.         accum += set_idx_array[i] * get_rule_index(i);
  403.         i++;
  404.          set_idx_array[i] = (rule_index - accum)/get_rule_index(i);
  405.         } // end loop through vars
  406.   
  407. } // end FuzzyModelBase::calc_rule_components()
  408.  
  409.   
  410. //
  411. // Function:    calc_rule_index_wrapper()
  412. // 
  413. // Purpose:        Calculates the rule_index value for all the variables
  414. //                and the variable's sets. See FuzzySetBase.h for a detailed
  415. //                explaination on how "rule_index" is used
  416. //
  417. // Arguments:
  418. //
  419. //        none
  420. //
  421. // Returns:
  422. //
  423. //        void
  424. //
  425. // Author:    Michael Zarozinski
  426. // Date:    6/00
  427. // 
  428. // Modification History
  429. // Author    Date        Modification
  430. // ------    ----        ------------
  431. //
  432. //
  433. void FuzzyModelBase::calc_rule_index_wrapper(void)
  434.      int        factor;        // factor we've calculated
  435.  
  436.     // go through all the input variables...
  437.     for (int i = 0; i < input_var_count; i++)
  438.         {
  439.         // get the rule index for this variable
  440.  
  441.         // 'i' is the index of the variable we want to find the rule_index for, BUT we incrment it by
  442.         // one because we do NOT want to include that variable in the calculation. rule_index only
  443.         // needs to take into account the variables after the one we are calculating for.
  444.      
  445.         factor = calc_rule_index(i + 1);
  446.  
  447.         // set the variable's rule_index
  448.         input_var_arr[i]->set_rule_index(factor);
  449.  
  450.         // now that we have the factor for the variable... 
  451.         // set the rule_index value for each set
  452.         for (int j = 0; j < input_var_arr[i]->get_num_of_sets(); j++)
  453.             {
  454.              input_var_arr[i]->set_rule_index(factor * j, j);
  455.             } // end loop through sets
  456.  
  457.         } // end loop through variables
  458.  
  459.  
  460. } // end FuzzyModelBase::calc_rule_index_wrapper()
  461.  
  462.  
  463. //
  464. // Function:    calc_rule_index()
  465. // 
  466. // Purpose:        Recursive function that calculates the rule_index for a variable.
  467. //                See FuzzySetBase.h for a detailed explaination rule_index and how it's used
  468. //
  469. // Arguments:
  470. //
  471. //        int var_idx - index of the variable to calc the rule_index for
  472. //
  473. // Returns:
  474. //
  475. //        int - the rule_index for the variable
  476. //
  477. // Author:    Michael Zarozinski
  478. // Date:    5/00
  479. // 
  480. // Modification History
  481. // Author    Date        Modification
  482. // ------    ----        ------------
  483. //
  484. //
  485. int FuzzyModelBase::calc_rule_index(int var_idx)
  486. {
  487.  
  488.     // last var... we're done recursing
  489.     if (var_idx >= input_var_count)
  490.         {
  491.         return 1;
  492.         }
  493.  
  494.     int    num_of_sets = input_var_arr[var_idx]->get_num_of_sets();
  495.  
  496.     // call ourselves recursively cuz we need to find the values for the variables below
  497.  
  498.     return(num_of_sets * calc_rule_index(var_idx + 1));
  499.  
  500. } // end  FuzzyModelBase::calc_rule_index()
  501.  
  502.  
  503. //
  504. // Function:    add_set()
  505. // 
  506. // Purpose:        Insert a set into the variable associated with the index
  507. //                passed in. This also adjusts the rules array appropriately.
  508. //                NOTE: that a COPY of the set passed in is inserted into 
  509. //                the var so the caller must free the object if it dynamically allocated it.
  510. //
  511. // Arguments:
  512. //
  513. //        int                    _var_idx    - index of the variable to insert the set into
  514. //        const FuzzySetBase* _set        - set to insert a copy of
  515. //
  516. // Returns:
  517. //
  518. //        0 - success
  519. //        non-zero - failure
  520. //
  521. // Author:    Michael Zarozinski
  522. // Date:    5/00
  523. // 
  524. // Modification History
  525. // Author    Date        Modification
  526. // ------    ----        ------------
  527. //
  528. //
  529. int FuzzyModelBase::add_set(int _var_idx,  const FuzzySetBase* _set) 
  530. {
  531.      RuleArrayType*        new_mem = NULL;        // pointer to the new memory
  532.     int                    new_mem_size;        // size of the new memory we're allocating
  533.     int                    output_index = 0;    // index of the output variable
  534.  
  535.     FuzzyVariableBase* var = get_var(_var_idx); 
  536.  
  537.     int ret_val = var->add_set(_set);  
  538.     
  539.     if (ret_val)
  540.         return ret_val;
  541.  
  542.     calc_rule_index_wrapper(); // re-calc rule_index values now that we have a new set
  543.  
  544.     // if this is an output var we don't need to assign any more memory
  545.     if (var->is_output())
  546.         return 0;
  547.  
  548.     // if we got to this point, we're dealing with an input variable
  549.  
  550.     // calc the size of the new memory block
  551.     new_mem_size = calc_num_of_rules();
  552.  
  553.     if (new_mem_size == 0)
  554.         return 0; // nothing more to do...
  555.  
  556.     // allocate and init the new memory for rules...
  557.     new_mem = new RuleArrayType[new_mem_size];;
  558.     memset(new_mem, NO_RULE, new_mem_size * sizeof(RuleArrayType));
  559.  
  560.     assert(new_mem != NULL);
  561.  
  562.     // IF we have rules to copy...
  563.  
  564.     if (rules->get_max())
  565.         {
  566.          // rather than stick the new memory in the middle of the existing mem we'll just
  567.         // copy the needed data...
  568.         int* set_idx_array = new int[get_input_var_count()]; // array of the sets involved in the current rule
  569.   
  570.         int    rule_index = 0;    // index for the rule into the OLD memory
  571.         
  572.          for (int new_mem_idx = 0; new_mem_idx < new_mem_size; new_mem_idx++)
  573.             {
  574.             // we've added the set so this gets the rule components for the
  575.             // rule index with the new set added
  576.             calc_rule_components(new_mem_idx, set_idx_array);
  577.  
  578.             // if this index involves the NEW set leave it as NO_RULE
  579.             if (set_idx_array[_var_idx] == _set->get_index())
  580.                  continue;
  581.  
  582.             // copy the rule from the OLD array to the NEW array
  583.                  
  584.             DOMType a = rules->get_rule(rule_index);
  585.             if (a != NO_RULE)
  586.                 int x = 23;
  587.             new_mem[new_mem_idx] = rules->get_rule(rule_index);
  588.  
  589.             // increment the index into the OLD rules...
  590.             rule_index++;
  591.  
  592.             } // end looop through new mem
  593.  
  594.         delete[] set_idx_array;
  595.         } // end if we have rules
  596.  
  597.     // now set the rules to the new rule array with the old rules copied in
  598.     rules->set(new_mem, new_mem_size);
  599.  
  600.     // free the new rules we allocated
  601.     delete[] new_mem;
  602.  
  603.     return 0;
  604.  
  605. } // end FuzzyModelBase::add_set()
  606.  
  607.  
  608. //
  609. // Function:    delete_set()
  610. // 
  611. // Purpose:        Delete a set from the variable associated with the index
  612. //                passed in. This also adjusts the rules array appropriately.
  613. //
  614. // Arguments:
  615. //
  616. //        int                    _var_idx    - index of the variable to remove the set from
  617. //        int                    _setr_idx    - index of the set to remove from the variable associated with _var_idx
  618. //
  619. // Returns:
  620. //
  621. //        0 - success
  622. //        non-zero - failure
  623. //
  624. // Author:    Michael Zarozinski
  625. // Date:    5/00
  626. // 
  627. // Modification History
  628. // Author    Date        Modification
  629. // ------    ----        ------------
  630. //
  631. //
  632. int FuzzyModelBase::delete_set(int _var_idx, int _set_idx)
  633. {
  634.      RuleArrayType*    new_mem = NULL;        // pointer to the new memory
  635.     int                    new_mem_size;        // size of the new memory we're allocating
  636.     int                    output_index = 0;    // index of the output variable
  637.      int ret_val;
  638.  
  639.     FuzzyVariableBase* var = get_var(_var_idx);  
  640.  
  641.     assert(var != NULL);
  642.  
  643.     // if this is an output var we don't need to remove any memory, but we
  644.     // need to adjust existing rules
  645.     if (var->is_output())
  646.         {
  647.         // go through all rules and remove any references to this rule
  648.         // AND if any rule is GREATER than the one we're deleting, decrement
  649.         // it to adjust for the removed rule
  650.  
  651.         RuleArrayType out_set;    // output set in rules array
  652.         
  653.         int num_rules = rules->get_max();
  654.  
  655.         for (int rule_index = 0; rule_index < num_rules; rule_index++)
  656.             {
  657.             out_set = rules->get_rule(rule_index);
  658.             if (out_set == _set_idx)
  659.                 rules->add_rule(rule_index, NO_RULE);
  660.             else if (out_set > _set_idx && out_set != NO_RULE)
  661.                 rules->add_rule(rule_index, out_set - 1);
  662.  
  663.             } // end loop through rules
  664.  
  665.         // now do the actual delete...
  666.         return var->delete_set(_set_idx); 
  667.  
  668.         } // end if output var
  669.  
  670.     // if we got to this point, we're dealing with an input var
  671.  
  672.     // calculate the new size of the memory. NOTE we'd normally use calc_num_of_rules() here
  673.     // but we haven't deleted the set yet so that would give us the wrong value
  674.     int _num_sets;
  675.     new_mem_size = 1; // init to 1 so we don't mult by 0
  676.     for (int i = 0; i < input_var_count; i++)     // don't include the outupt var
  677.         {
  678.         // make sure we don't multply by 0...
  679.         _num_sets = input_var_arr[i]->get_num_of_sets();
  680.         if (i == _var_idx)
  681.             _num_sets--;    // SUBTRACT for the set we're deleting
  682.         if (_num_sets)
  683.             new_mem_size *= _num_sets;
  684.  
  685.         } // end loop through input variables
  686.  
  687.     // allocate and init the new memory...
  688.      new_mem = new RuleArrayType[new_mem_size];
  689.  
  690.     if (!new_mem)
  691.         {
  692.          set_msg_text(ERR_ALLOC_MEM);
  693.          assert(new_mem != NULL);
  694.         return -1;
  695.         } // end if error allocating memory
  696.  
  697.     memset(new_mem, NO_RULE, new_mem_size * sizeof(RuleArrayType));
  698.     
  699.     // copy all the rules that do NOT include the set being deleted
  700.     int new_rule_index = 0;
  701.  
  702.     int* set_idx_array = new int[get_input_var_count()]; // array of the sets involved in the current rule
  703.  
  704.     for (int rule_index = 0; rule_index < rules->get_max(); rule_index++)
  705.         {
  706.         // get the sets that make up this rule
  707.  
  708.           calc_rule_components(rule_index, set_idx_array);
  709.  
  710.         // if the rule does NOT involve the set we're going to delete, copy it
  711.  
  712.         if (set_idx_array[_var_idx] == _set_idx)
  713.             continue;
  714.  
  715.         // set that new index to the rule that should be there
  716.         new_mem[new_rule_index] = rules->get_rule(rule_index);
  717.  
  718.         new_rule_index++; // increment NEW rule index
  719.  
  720.         } // end loop through rules
  721.  
  722.     delete[] set_idx_array;
  723.  
  724.      // now copy the new memory into the old rules block...
  725.     rules->set(new_mem, new_mem_size);
  726.  
  727.     delete[] new_mem;    // free the memory we allocated
  728.  
  729.     // now do the actual delete...
  730.     ret_val = var->delete_set(_set_idx); 
  731.  
  732.     // re-calc at the variable index factors...
  733.     calc_rule_index_wrapper();
  734.  
  735.     return ret_val;
  736.  
  737. } // end FuzzyModelBase::delete_set()
  738.  
  739.  
  740. //
  741. // Function:    is_var_id_unique
  742. // 
  743. // Purpose:        Checks if the variable name passed in is unique for this model
  744. //
  745. // Arguments:
  746. //
  747. //        const wchar_t*    _id            - variable name to check
  748. //        int                _var_idx    - index for the variable the id belongs to
  749. //
  750. // Returns:
  751. //
  752. //        true - id is unique
  753. //        false - id is NOT unique
  754. //
  755. // Author:    Michael Zarozinski
  756. // Date:    6/00
  757. // 
  758. // Modification History
  759. // Author    Date        Modification
  760. // ------    ----        ------------
  761. //
  762. //
  763. bool FuzzyModelBase::is_var_id_unique(const wchar_t* _id, int _var_idx) const
  764.     {
  765.     FuzzyVariableBase* var;    // pointer to variable
  766.  
  767.     // strip off any leading/trailing spaces...
  768.  
  769.     // make sure our id is UNIQUE within the rule block
  770.      for (int i = 0; i < input_var_count; i++)
  771.         {
  772.         if (i == _var_idx)
  773.             continue;    // don't check the var the id is for
  774.  
  775.         var = get_var(i);
  776.  
  777.         // do a case insensitive comparison
  778.         if (wcsicmp(var->get_id(), _id) == 0)
  779.             {
  780.             // NOT unique
  781.             set_msg_text( load_string(ERR_VAR_NON_UNIQUE_ID));
  782.             return false;
  783.             }
  784.  
  785.         } // end loop through vars
  786.  
  787.     // check ouput var - UNLESS the id is FOR an output var
  788.     if (_var_idx != OUTPUT_IDX && output_var)
  789.         {
  790.         var = get_var(OUTPUT_IDX);
  791.  
  792.         // do a case insensitive comparison
  793.         if (wcsicmp(var->get_id(), _id) == 0)
  794.             {
  795.             // NOT unique
  796.             set_msg_text(ERR_VAR_NON_UNIQUE_ID);
  797.             return false;
  798.             }
  799.  
  800.         } // end if not output var
  801.  
  802.     return true;
  803.  
  804. } // end FuzzyModelBase::is_var_id_unique()
  805.  
  806.  
  807. //
  808. // Function:    get_msg_textA()
  809. // 
  810. // Purpose:        This fucntion gets the msg_text (from FFLLBase) and 
  811. //                returns the ascii version of it. 
  812. //
  813. // Arguments:
  814. //
  815. //            none
  816. // 
  817. // Returns:
  818. //
  819. //            const char* - ansii version of msg_text
  820. //
  821. //
  822. // Author:    Michael Zarozinski
  823. // Date:    9/01
  824. // 
  825. // Modification History
  826. // Author    Date        Modification
  827. // ------    ----        ------------
  828. //
  829. //
  830. const char* FuzzyModelBase::get_msg_textA()
  831. {
  832.     ascii_err_msg = "";    // clear out error message
  833.  
  834.     char* aid = convert_to_ascii(get_msg_text());
  835.  
  836.     if (aid == NULL)
  837.         return NULL;
  838.  
  839.     ascii_err_msg = aid;
  840.  
  841.     delete[] aid;
  842.  
  843.      return ascii_err_msg.data();
  844.     
  845. } // end get_msg_textA()
  846.  
  847.  
  848. //
  849. // Function:    calc_output()
  850. // 
  851. // Purpose:        Calculates the defuzzified output value for the model
  852. //
  853. // Arguments:
  854. //
  855. //        short*        var_idx_arr        -    Array that holds the current index value 
  856. //                                        for each input var
  857. //        DOMType*    out_set_dom_arr -    Array that holds the DOM value for each
  858. //                                        set in the output variable
  859. //
  860. // Returns:
  861. //
  862. //        RealType - the output value, FLT_MIN if no ouput set is active
  863. //
  864. // Author:    Michael Zarozinski
  865. // Date:    
  866. // 
  867. // Modification History
  868. // Author    Date        Modification
  869. // ------    ----        ------------
  870. //
  871. //
  872. RealType FuzzyModelBase::calc_output(short* var_idx_arr, DOMType* out_set_dom_arr )  
  873. {
  874.     calc_active_output_level_wrapper(var_idx_arr, out_set_dom_arr);
  875.  
  876.     if (!output_var)
  877.         return FLT_MIN;    // don't have an output var yet!
  878.  
  879.     return output_var->calc_output_value(out_set_dom_arr);
  880.  
  881. } // end FuzzyModelBase::calc_output()
  882.  
  883.  
  884.  
  885. //
  886. // Function:    validate_fcl_identifier()
  887. // 
  888. // Purpose:        Validate that the identifier is in valid FCL format. The IEC 61131-7 
  889. //                specifies identifiers as :
  890. //            
  891. //                    identifier
  892. //                        {alpha}{alphanum}*(\[{alphanum}\])?
  893. //                        (\.{alpha}{alphanum}*(\[{alphanum}\])?)* |
  894. //                        {alpha}{alphanum}*(\[({digits}..{digits})(,({digits}..{digits}))*\])
  895. // 
  896. //                    where
  897. //
  898. //                        alpha ::= [a-zA-Z_]
  899. //                        alphanum ::= [a-zA-Z_0-9]
  900. //
  901. //                You'll note that this does not handle wild characters.
  902. //
  903. //                This currently only checks for spaces or an identifier that starts with
  904. //                a number. This could be made much more robust.
  905. //
  906. //                If there's something wrong with the identifier the problem is written out in
  907. //                a comment.
  908. //
  909. // Arguments:
  910. //
  911. //        std::ofstream&    file_contents    -    file we're writing to
  912. //        std::string        identifier        -    identifier to check
  913. //
  914. // Returns:
  915. //
  916. //        void
  917. //
  918. // Author:    Michael Zarozinski
  919. // Date:    9/01
  920. // 
  921. // Modification History
  922. // Author    Date        Modification
  923. // ------    ----        ------------
  924. //
  925. //
  926. void FuzzyModelBase::validate_fcl_identifier(std::ofstream& file_contents, std::string identifier)
  927. {
  928.  
  929.      bool add_comment = false;
  930.  
  931.     // if there's a space in the identifier - flag an error
  932.     if (identifier.find(" ") != -1)
  933.         {
  934.         file_contents << "\t(* Identifier should not have any spaces, they were replaced with underscores ";
  935.         add_comment = true;
  936.         }
  937.  
  938.     if (static_cast<int>(identifier[0]) < 9)
  939.         {
  940.         if (add_comment)
  941.             file_contents << "or start with a digit ";
  942.         else
  943.             file_contents << "\t(* Identifier should not start with a digit ";
  944.         add_comment = true;
  945.         }
  946.  
  947.     if (add_comment)
  948.         file_contents << "*)";
  949.  
  950.  
  951. } // end FuzzyModelBase::validate_fcl_identifier()
  952.   
  953.  
  954.  
  955. //
  956. // Function:    calc_active_output_level_wrapper()
  957. // 
  958. // Purpose:        Wrapper function that calculates the active output level
  959. //                for each output set.
  960. //
  961. // Arguments:
  962. //
  963. //        short*        var_idx_arr        -    Array that holds the current index value 
  964. //                                        for each input var
  965. //        DOMType*    out_set_dom_arr -    Array that we're writing to. It holds the DOM value for each
  966. //                                        set in the output variable
  967. //
  968. // Returns:
  969. //
  970. //        void
  971. //
  972. // Author:    Michael Zarozinski
  973. // Date:    5/99
  974. // 
  975. // Modification History
  976. // Author    Date        Modification
  977. // ------    ----        ------------
  978. //
  979. //
  980. void FuzzyModelBase::calc_active_output_level_wrapper(short* var_idx_arr, DOMType* out_set_dom_arr )   
  981. {
  982.       if (!output_var)
  983.         return;    // don't have an output var yet!
  984.  
  985.     // zero out the dom arrays...
  986.     for (int i = 0; i < output_var->get_num_of_sets(); i++)
  987.         {
  988.         out_set_dom_arr[i] = 0;  
  989.         }
  990.  
  991.     // call recursive func to calc each sets DOM...
  992.       calc_active_output_level(0, 0, 0, var_idx_arr, out_set_dom_arr);
  993.  
  994. } // end FuzzyModelBase::calc_active_output_level_wrapper()
  995.  
  996.  
  997. //
  998. // Function:    add_input_var_to_list()
  999. // 
  1000. // Purpose:        Add the variable passed in to the list of input variables and
  1001. //                adjust the rules as needed.
  1002. //                Note that this does not COPY the var passed in, it sets the
  1003. //                input variable array to point to the var passed in.
  1004. //
  1005. // Arguments:
  1006. //
  1007. //        FuzzyVariableBase* var - variable to insert
  1008. //
  1009. // Returns:
  1010. //
  1011. //        void
  1012. //
  1013. // Author:    Michael Zarozinski
  1014. // Date:    8/01
  1015. // 
  1016. // Modification History
  1017. // Author    Date        Modification
  1018. // ------    ----        ------------
  1019. //
  1020. //
  1021. void FuzzyModelBase::add_input_var_to_list(FuzzyVariableBase* var )
  1022. {
  1023.     // need to create new memory then we'll copy the old to it...
  1024.     FuzzyVariableBase** tmp_var = new FuzzyVariableBase*[input_var_count + 1]; // new mem
  1025.  
  1026.     // copy the old memory to the new mem..
  1027.     for (int i = 0; i < input_var_count; i++)
  1028.         tmp_var[i] = input_var_arr[i];
  1029.  
  1030.     // add this variable to the variable array....
  1031.  
  1032.      var->set_index(input_var_count);
  1033.     tmp_var[input_var_count] = var; 
  1034.  
  1035.      // set the index in var so we know where we are in the array
  1036.  
  1037.     input_var_count++;  // increment the number of variables we have
  1038.     
  1039.     // set the old mem to the new mem...
  1040.  
  1041.     delete[] input_var_arr; // free old mem
  1042.  
  1043.     input_var_arr = tmp_var;
  1044.  
  1045.     // if there are no sets for the new var... no need to expand memory
  1046.     if (var->get_num_of_sets() == 0)
  1047.         {
  1048.         // clear rules before going on...
  1049.         rules->clear();
  1050.         return;
  1051.         }
  1052.  
  1053.     // calc the size of the new memory block
  1054.     // NOTE:  we've already added the set so the num_of_sets has been incremented
  1055.      int     new_mem_size = calc_num_of_rules();
  1056.  
  1057.     // if there's any var that has NO sets... new_mem_size will be 0... this is ok,
  1058.     // cuz that means we CAN'T have any rules yet!
  1059.     if (new_mem_size == 0)
  1060.         return; // nothing more to do...
  1061.  
  1062.     // anytime we add a new variable (even if we're copying an existing one) we
  1063.     // loose all the rules so we just call alloc
  1064.  
  1065.     rules->alloc(new_mem_size);
  1066.     
  1067.      // re-calc at the variable index factors...
  1068.  
  1069.     calc_rule_index_wrapper();
  1070.  
  1071. } // end FuzzyModelBase::add_input_var_to_list()
  1072.  
  1073.  
  1074. //
  1075. // Function:    calc_active_output_level()
  1076. // 
  1077. // Purpose:        Recursive function that calculates the DOMs for the output sets.
  1078. //
  1079. // Arguments:
  1080. //
  1081. //        int            var_num                -    variable we're checking the DOMs for
  1082. //        DOMType        activation_level    -    current activation level 
  1083. //        int            rule_index            -    index for the rule we're checking DOMs for
  1084. //        short*        var_idx_arr            -    Array that holds the current index value 
  1085. //                                            for each input var
  1086. //        DOMType*    out_set_dom_arr        -    Array that holds the DOM value for each
  1087. //                                            set in the output variable
  1088. //
  1089. // Returns:
  1090. //
  1091. //        void
  1092. //
  1093. // Author:    Michael Zarozinski
  1094. // Date:    7/99
  1095. // 
  1096. // Modification History
  1097. // Author    Date        Modification
  1098. // ------    ----        ------------
  1099. //
  1100. //
  1101. void FuzzyModelBase::calc_active_output_level(int var_num, DOMType activation_level, int rule_index, short* var_idx_arr , DOMType* out_set_dom_arr )   
  1102. {
  1103.  
  1104.       DOMType    old_activation_level;    // need to save the old actiavation
  1105.                                     // level in case we have more than 
  1106.                                     // one active set in this var
  1107.             
  1108.     DOMType        set_dom;        // var to hold the current set's dom.
  1109.  
  1110.     // if the 'var_num' is greater than the number of input variables - set it to the OUTPUT var,
  1111.     // that will end the recursing through this function
  1112.  
  1113.     if (var_num >= input_var_count)
  1114.         var_num = OUTPUT_IDX;
  1115.  
  1116.     // get the variable...
  1117.     const FuzzyVariableBase* var = get_var(var_num);
  1118.     assert(var != NULL);
  1119.  
  1120.     // loop through the sets for this var...
  1121.     int num_of_sets = var->get_num_of_sets();
  1122.   
  1123.     // loop through each set in this variable
  1124.     for (int i = 0; i < num_of_sets; i++)
  1125.         {
  1126.  
  1127.         if (!(var->is_output())) 
  1128.             {
  1129.             // input variable...
  1130.  
  1131.               set_dom = var->get_dom(i, (var_idx_arr[var_num])); 
  1132.              
  1133.             // if this set is not active - skip it...
  1134.  
  1135.             if (set_dom == 0)         
  1136.                 continue;
  1137.  
  1138.              // we need to deal with this set, save the activation level...
  1139.               old_activation_level = activation_level;
  1140.  
  1141.             // if this is the FIRST time through, set the activation level
  1142.             if (var_num == 0)
  1143.                 activation_level = set_dom;
  1144.  
  1145.             // set the activation level to the current set's level dependent on the inference method
  1146.              if ((inference_method == INFERENCE_OPERATION::MIN) && (set_dom < activation_level) )
  1147.                 activation_level = set_dom;
  1148.             else if ((inference_method == INFERENCE_OPERATION::MAX) && (set_dom > activation_level))
  1149.                 activation_level = set_dom;
  1150.  
  1151.  
  1152.             // get the set we're dealing with for the recursive call
  1153.             FuzzySetBase* set = var->get_set(i);
  1154.  
  1155.             // call recursively, incrementing the variable index by one and adding
  1156.             // this set's rule_index to the one passed in.
  1157.             calc_active_output_level(var_num + 1, activation_level, rule_index + set->get_rule_index(), var_idx_arr, out_set_dom_arr);  
  1158.  
  1159.             }
  1160.         else
  1161.             {
  1162.             // we're dealing with the output variable
  1163.  
  1164.             // we need to deal with this set, save the activation level...
  1165.               old_activation_level = activation_level;
  1166.  
  1167.             // if there is a rule for the rule_index passed in, set the output set's DOM
  1168.             if (rule_exists(rule_index))
  1169.                 {
  1170.                 // SUB 1 from activation level cuz that's from 0 to MAX_DOM and
  1171.                 // we're setting an INDEX
  1172.  
  1173.                 set_output_dom(out_set_dom_arr, rules->get_rule(rule_index), activation_level - 1);
  1174.                 }
  1175.             
  1176.             } // end if output var
  1177.  
  1178.         // reset the activation level...  
  1179.           activation_level = old_activation_level;
  1180.  
  1181.         } // end loop through sets
  1182.  
  1183. } // end FuzzyModelBase::calc_active_output_level()
  1184.  
  1185.  
  1186. //
  1187. // Function:    save_to_fcl_file()
  1188. // 
  1189. // Purpose:        Main function to save the model in the IEC 61131-7 International Standard 
  1190. //                FCL (Fuzzy Control Language)
  1191. //
  1192. // Arguments:
  1193. //
  1194. //        const char* file_name - path and file name to save the information to.
  1195. //
  1196. // Returns:
  1197. //
  1198. //        0 - success
  1199. //        non-zero - failure 
  1200. //
  1201. // Author:    Michael Zarozinski
  1202. // Date:    9/01
  1203. // 
  1204. // Modification History
  1205. // Author    Date        Modification
  1206. // ------    ----        ------------
  1207. //
  1208. // 
  1209.  
  1210. int FuzzyModelBase::save_to_fcl_file(const char* file_name)
  1211. {
  1212.     int                i;                // counter
  1213.      std::ofstream    file_contents;    // content of the file
  1214.  
  1215.     // open the file
  1216.     file_contents.open(file_name); 
  1217.  
  1218.     if (!(file_contents.is_open()))
  1219.         {
  1220.         set_msg_text(ERR_OPENING_FILE); // could be a little more robuts here
  1221.         return -1;
  1222.         }
  1223.  
  1224.     time_t tmp_time;    // time structure
  1225.     char date[100];        // temp string
  1226.  
  1227.     time(&tmp_time);
  1228.     struct tm *newtime = localtime( &tmp_time );  // Convert time to "struct tm" form 
  1229.  
  1230.     // asctime returns EXACTLY 26 characters... the last 2 of which are a new line and null
  1231.     // we don't want them, so only print the first 24 chars...
  1232.     sprintf(date, "%.24s", asctime( newtime ) );
  1233.  
  1234.     file_contents << "(* FCL File Created From FFLL Model: " << date << " *)\n\n";
  1235.  
  1236.     // start the FCL block
  1237.      file_contents << get_fcl_block_start() << "\n\n";
  1238.  
  1239.     // start the input variable block
  1240.     file_contents << FuzzyVariableBase::get_fcl_block_start() << "\n";
  1241.  
  1242.     // save input variables
  1243.      for (i = 0; i < input_var_count; i++)
  1244.             input_var_arr[i]->save_var_to_fcl_file(file_contents); 
  1245.  
  1246.     // end the input variable block
  1247.     file_contents << FuzzyVariableBase::get_fcl_block_end() << "\n\n";
  1248.  
  1249.      // save output var
  1250.     file_contents << FuzzyOutVariable::get_fcl_block_start() << "\n";
  1251.     output_var->save_var_to_fcl_file(file_contents);
  1252.     file_contents << FuzzyOutVariable::get_fcl_block_end() << "\n\n";
  1253.  
  1254.     // write input vars' out sets...
  1255.     for (i = 0; i < input_var_count; i++)
  1256.            input_var_arr[i]->save_sets_to_fcl_file(file_contents); 
  1257.  
  1258.     // write out the output var's sets
  1259.     output_var->save_sets_to_fcl_file(file_contents);
  1260.  
  1261.     // write out the rules
  1262.      save_rules_to_fcl_file(file_contents);
  1263.  
  1264.     // end the FCL block
  1265.     file_contents << get_fcl_block_end() <<  "\n\n";
  1266.  
  1267.     // close the file
  1268.     file_contents.close();
  1269.  
  1270.     return 0;
  1271.  
  1272. } // end FuzzyModelBase::save_to_fcl_file( )
  1273.  
  1274.  
  1275.  
  1276. //
  1277. // Function:    save_rules_to_fcl_file()
  1278. // 
  1279. // Purpose:        Save rules to a file according to the IEC 61131-7 International Standard 
  1280. //                FCL (Fuzzy Control Language)
  1281. //
  1282. // Arguments:
  1283. //
  1284. //        std::ofstream& file_contents - file we're writing to in stream form
  1285. //
  1286. // Returns:
  1287. //
  1288. //        void
  1289. //
  1290. // Author:    Michael Zarozinski
  1291. // Date:    9/01
  1292. // 
  1293. // Modification History
  1294. // Author    Date        Modification
  1295. // ------    ----        ------------
  1296. //
  1297. // 
  1298.  
  1299. void FuzzyModelBase::save_rules_to_fcl_file(std::ofstream& file_contents) const
  1300. {
  1301.     int i;    // counter
  1302.  
  1303.     // start the 'ruleblock'
  1304.     file_contents << "RULEBLOCK first\n";    // name 'first' cuz we have no better name
  1305.  
  1306.     // print out inference method
  1307.  
  1308.     int inference = get_inference_method();
  1309.  
  1310.      file_contents << "\t";
  1311.  
  1312.     if (inference == FuzzyModelBase::INFERENCE_OPERATION::MIN)  
  1313.         file_contents << "AND:MIN";
  1314.     else if (inference == FuzzyModelBase::INFERENCE_OPERATION::MAX) 
  1315.         file_contents << "OR:MAX";
  1316.  
  1317.     file_contents << ";\n";
  1318.  
  1319.     // print out composition method
  1320.  
  1321.     int composition = get_composition_method();
  1322.     
  1323.     file_contents << "\tACCUM:";
  1324.  
  1325.       if (composition == FuzzyOutVariable::COMPOSITION_OPERATION::MIN)  
  1326.         file_contents << "BSUM";
  1327.     else if (composition == FuzzyOutVariable::COMPOSITION_OPERATION::MAX) 
  1328.         file_contents << "MAX";
  1329.  
  1330.     file_contents << ";\n";
  1331.  
  1332.     // list the rules, if a rule does NOT exist we'll put a comment in to clearly indicate
  1333.     // that a rule is missing
  1334.  
  1335.     // create an array for each variable to hold the sets, we'll do it dynamically rather
  1336.     // than use an STL container cuz we know the sizes before we start
  1337.  
  1338.     std::string** var_sets;
  1339.  
  1340.     var_sets = new std::string*[input_var_count + 1];    // add one for output var
  1341.  
  1342.     // create mem for each var...
  1343.     int num_of_sets;
  1344.     FuzzyVariableBase* var;
  1345.  
  1346.     // loop through input vars, we'll save each set's ID into the var_sets array
  1347.     for (i = 0; i < input_var_count; i++)
  1348.         {
  1349.         var = get_var(i);
  1350.         num_of_sets = var->get_num_of_sets();
  1351.  
  1352.         var_sets[i] = new std::string[num_of_sets];
  1353.  
  1354.         // loop through this variable's sets
  1355.         for (int j = 0; j < num_of_sets; j++)
  1356.             {
  1357.             FuzzySetBase* set = var->get_set(j);
  1358.  
  1359.             char* aid = convert_to_ascii(set->get_id(), '_');
  1360.  
  1361.             var_sets[i][j] = aid;
  1362.  
  1363.             delete[] aid;
  1364.  
  1365.             } // end loop through sets
  1366.  
  1367.         } // end loop through vars
  1368.  
  1369.     // add output var's sets...
  1370.     var = get_var(OUTPUT_IDX);
  1371.     num_of_sets = var->get_num_of_sets();
  1372.  
  1373.     // NOTE: we use the 'i' counter from above
  1374.     var_sets[i] = new std::string[num_of_sets];
  1375.  
  1376.     for (int j = 0; j < num_of_sets; j++)
  1377.         {
  1378.          char* aid = convert_to_ascii(var->get_id(j), '_');
  1379.         
  1380.         var_sets[i][j] = aid;
  1381.  
  1382.         delete[] aid;
  1383.  
  1384.         } // end loop through sets
  1385.  
  1386.      // loop through rules and write them out...
  1387.  
  1388.     RuleArrayType rule;    // rule we're dealing with
  1389.     int*    set_idx_array = new int[input_var_count];
  1390.     char    tmp[4];
  1391.      for (i = 0; i < get_num_of_rules(); i++)
  1392.         {
  1393.         rule = rules->get_rule(i);
  1394.  
  1395.         file_contents << "\t";
  1396.  
  1397.         if (rule == NO_RULE)
  1398.             {
  1399.             // converting int to string cuz MSVC reports a memory leak if we don't and
  1400.             // use '<< i <<' below. I'm sure this is another "false leak" that MSVC is
  1401.             // prone to reporting when STL objects are used.
  1402.             sprintf(tmp, "%d", i);         
  1403.               file_contents << "(* RULE " << tmp << " : No Rule Specified *)";
  1404.  
  1405.             }
  1406.         else
  1407.             {
  1408.             // calculate the components of the rule...
  1409.             calc_rule_components(i, set_idx_array);
  1410.  
  1411.             sprintf(tmp, "%d", i);         
  1412.                          
  1413.             file_contents << "RULE " << tmp << ": IF ";
  1414.  
  1415.             // print out the IDs of the sets involved in this rule
  1416.             for (int j = 0; j < input_var_count; j++)
  1417.                 {
  1418.                  file_contents << var_sets[j][set_idx_array[j]];
  1419.  
  1420.                 if (j < input_var_count - 1)
  1421.                     file_contents << " AND ";
  1422.                 } // end loop through vars
  1423.  
  1424.              file_contents << " THEN " << var_sets[j][rule];
  1425.  
  1426.             } // define rule
  1427.  
  1428.         file_contents << ";\n";
  1429.  
  1430.         } // end loop through rules
  1431.  
  1432.     file_contents << "END_RULEBLOCK\n\n"; 
  1433.  
  1434.     // free all the memory we allocated
  1435.     delete[] set_idx_array;
  1436.  
  1437.     for (i = 0; i < input_var_count + 1; i++)
  1438.         {
  1439.         delete[] var_sets[i];
  1440.         }
  1441.  
  1442.     delete[] var_sets;
  1443.  
  1444. } // end FuzzyModelBase::save_rules_to_file()
  1445.  
  1446.  
  1447. //
  1448. // Function:    load_from_fcl_file()
  1449. // 
  1450. // Purpose:        Main function to read in an FCL file and create a model.
  1451. //                NOTE: It is a BRAIN-DEAD parser. 
  1452. // Arguments:
  1453. //
  1454. //        const char* file_name - file to read
  1455. //
  1456. // Returns:
  1457. //
  1458. //        0 - success
  1459. //        non-zero - failure
  1460. //
  1461. // Author:    Michael Zarozinski
  1462. // Date:    9/01
  1463. // 
  1464. // Modification History
  1465. // Author    Date        Modification
  1466. // ------    ----        ------------
  1467. //
  1468. // 
  1469.  
  1470. int FuzzyModelBase::load_from_fcl_file(const char* file_name)
  1471. {
  1472.     // NOTE: in some cases MSVC reports this is a memory leak,
  1473.     // as stated in _Visual C++ Developers Journal_ (March 2000) p. 50
  1474.     // when using STL false memory leaks can be reported because
  1475.     // iostream objects are constructed before main() starts and destructed
  1476.     // after mian() exits so the memory leak detection will report a false leak
  1477.  
  1478.      std::ifstream file_contents(file_name);
  1479.  
  1480.      if (!(file_contents.is_open()))
  1481.         {
  1482.         set_msg_text(ERR_OPENING_FILE); // could be a little more robuts here
  1483.         return -1;
  1484.         }
  1485.  
  1486.     // load input variables
  1487.     if (load_vars_from_fcl_file(file_contents))
  1488.         return -1;    // error is written to msg_txt in the called func
  1489.  
  1490.     // load the output variable
  1491.      if (load_vars_from_fcl_file(file_contents, true))  
  1492.         return -1;    // error is written to msg_txt in the called func
  1493.  
  1494.     // load sets for each var
  1495.     for (int i = 0; i < input_var_count; i++)
  1496.         {
  1497.            if (input_var_arr[i]->load_sets_from_fcl_file(file_contents))
  1498.             {
  1499.             // get the message text and set it for the model
  1500.             set_msg_text(input_var_arr[i]->get_msg_text());
  1501.             return -1;
  1502.             }
  1503.         } // end loop through input vars
  1504.  
  1505.     if (output_var)
  1506.         {
  1507.         if (output_var->load_sets_from_fcl_file(file_contents))
  1508.             {
  1509.             // get the message text and set it for the model
  1510.             set_msg_text(output_var->get_msg_text());
  1511.             return -1;
  1512.             }
  1513.         } // end if output var
  1514.  
  1515.     if (load_rules_from_fcl_file(file_contents))
  1516.         return -1;    // error is written to msg_txt in the called func
  1517.  
  1518.     if (load_defuzz_block_from_fcl_file(file_contents))
  1519.         return -1;    // error is written to msg_txt in the called func
  1520.  
  1521.     return 0;
  1522.  
  1523.  
  1524. } // end FuzzyModelBase::load_from_fcl_file()
  1525.  
  1526.  
  1527. //
  1528. // Function:    load_vars_from_fcl_file()
  1529. // 
  1530. // Purpose:        Loads the variables from the FCL file and creates them.
  1531. //
  1532. // Arguments:
  1533. //
  1534. //        std::ifstream&    file_contnts    - file to read from
  1535. //        bool            output_ind        -    indicates if we're reading the output variable
  1536. //
  1537. // Returns:
  1538. //
  1539. //        0 - success
  1540. //        non-zero - failure
  1541. //
  1542. // Author:    Michael Zarozinski
  1543. // Date:    9/01
  1544. // 
  1545. // Modification History
  1546. // Author    Date        Modification
  1547. // ------    ----        ------------
  1548. //
  1549. //    
  1550.  
  1551. int FuzzyModelBase::load_vars_from_fcl_file(std::ifstream& file_contents, bool output /* = false */)
  1552. {
  1553.         
  1554.     const char* start_token;// starting token (depends on input or output)
  1555.     const char* end_token;    // ending token (depends on input or output)
  1556.  
  1557.     if (output)
  1558.         {
  1559.         start_token = FuzzyOutVariable::get_fcl_block_start();
  1560.         end_token = FuzzyOutVariable::get_fcl_block_end();
  1561.         }
  1562.     else
  1563.         {
  1564.         start_token = FuzzyVariableBase::get_fcl_block_start();
  1565.         end_token = FuzzyVariableBase::get_fcl_block_end();
  1566.         }
  1567.  
  1568.      file_contents.seekg(0);    // go to start of file
  1569.  
  1570.      std::string token;
  1571.  
  1572.     do 
  1573.         {
  1574.         file_contents >> token;
  1575.         // if token is empty we reached EOF
  1576.         if (file_contents.eof())
  1577.             {
  1578.             set_msg_text(ERR_EOF_READING_VARS);
  1579.              return -1; 
  1580.             }
  1581.         }while (strcmp(token.data(), start_token) != 0);
  1582.  
  1583.  
  1584.       // parse names...
  1585.     // AGAIN... this is BRAIN-DEAD parsing, we're assuming the 
  1586.     // input vars have the format:
  1587.     //
  1588.     // VAR_INPUT  
  1589.     //        variable_1_name: REAL;
  1590.     //                .
  1591.     //                .
  1592.     //                .
  1593.     //        variable_n_name: REAL;
  1594.     // END_VAR
  1595.     //
  1596.     // Output varaible declaration is:
  1597.     //
  1598.     // VAR_OUTPUT  
  1599.     //        variable_name: REAL;
  1600.     // END_VAR
  1601.     //
  1602.     // NOTE: the IEC 61131-7 does not specify a way to set the range of a varaible, so 
  1603.     // we write them out in a range comment
  1604.  
  1605.      char line[300];
  1606.     char var_name[50];
  1607.     RealType start_val, end_val;    // range for the variable
  1608.  
  1609.     // get line-by-line until we reach END_VAR
  1610.     file_contents.getline(line, 300);
  1611.  
  1612.     while (strncmp(line, end_token, strlen(end_token)) != 0)
  1613.         {
  1614.         start_val = end_val = FLT_MIN; // init so we know if we have a range for the variable
  1615.  
  1616.          var_name[0] = '\0';
  1617.  
  1618.         // defined a var... find "REAL" in it and anything before that is the variable name
  1619.         sscanf(line, "%s", var_name);
  1620.  
  1621.         if (strlen(var_name) > 0) // deal with lines that are just spaces
  1622.             {
  1623.              // find the range of the variable
  1624.               
  1625.             // find "RANGE"
  1626.             char* pos = strstr(line, "RANGE(");
  1627.  
  1628.             char* num_start;
  1629.             char* num_end;
  1630.             char* stop_scan; // used for strtod
  1631.  
  1632.             if (pos != NULL)
  1633.                 {
  1634.                 // found "RANGE"... set position to start of first number
  1635.  
  1636.                 pos += strlen("RANGE(");
  1637.  
  1638.                 // found start of 1st number, save position
  1639.                 num_start = pos;
  1640.  
  1641.                 // find ".."
  1642.                 pos = num_end = strstr(pos, "..");
  1643.              
  1644.                 // get the substring and convert...
  1645.                 char value[50];
  1646.                 int num_len = num_end - num_start;
  1647.                 
  1648.                 memset(value, '\0', 50);
  1649.  
  1650.                 strncpy(value, num_start, num_len);
  1651.  
  1652.                 start_val = strtod(value, &stop_scan);
  1653.  
  1654.                 if (fabs(start_val) == HUGE_VAL)
  1655.                     {
  1656.                     // error converting...
  1657.                     set_msg_text(ERR_VAR_MIN_VALUE);
  1658.                      return -1; 
  1659.                     }
  1660.  
  1661.                 pos += strlen(".."); // puts us at the start of the next number
  1662.  
  1663.                 // find the ending ')'
  1664.                 num_start = pos;
  1665.  
  1666.                 num_end = strstr(pos, ")");
  1667.  
  1668.                 memset(value, '\0', 50);
  1669.  
  1670.                 num_len = num_end - num_start;
  1671.  
  1672.                 strncpy(value, num_start, num_len);
  1673.  
  1674.                 end_val = strtod(value, &stop_scan);
  1675.  
  1676.                 if (fabs(end_val) == HUGE_VAL)
  1677.                     {
  1678.                     // error converting...
  1679.                     set_msg_text(ERR_VAR_MAX_VALUE);
  1680.                      return -1; 
  1681.                     }
  1682.  
  1683.                 } // end found token RANGE
  1684.  
  1685.             int ret_val;    // holds return value
  1686.  
  1687.             // convert var_name to wide chars...
  1688.             wchar_t* wname = convert_to_wide_char(var_name);
  1689.  
  1690.             // create the variable
  1691.             if (output)
  1692.                 {
  1693.                 ret_val = add_output_variable(wname, start_val, end_val);
  1694.                 }
  1695.             else
  1696.                 {
  1697.                 ret_val = add_input_variable(wname, start_val, end_val);
  1698.                 }
  1699.  
  1700.             delete[] wname;
  1701.  
  1702.             if (ret_val)
  1703.                 return -1; // error is written in called func
  1704.  
  1705.             } // end if line is not null
  1706.  
  1707.         // get the next line
  1708.         file_contents.getline(line, 300);
  1709.  
  1710.         }; // end while not END_VAR
  1711.  
  1712.     return 0;
  1713.  
  1714. } // end FuzzyModelBase::load_input_vars_from_file()
  1715.  
  1716. //
  1717. // Function:    load_rules_from_fcl_file()
  1718. // 
  1719. // Purpose:        Loads the rules from the FCL file and creates them.
  1720. //
  1721. // Arguments:
  1722. //
  1723. //        std::ifstream&    file_contnts    - file to read from
  1724. //
  1725. // Returns:
  1726. //
  1727. //        0 - success
  1728. //        non-zero - failure
  1729. //
  1730. // Author:    Michael Zarozinski
  1731. // Date:    9/01
  1732. // 
  1733. // Modification History
  1734. // Author    Date        Modification
  1735. // ------    ----        ------------
  1736. //
  1737. //    
  1738.  
  1739. int FuzzyModelBase::load_rules_from_fcl_file(std::ifstream& file_contents)
  1740. {
  1741.     // a very brain-dead parser of the RULEBLOCK section, this assumes that we have
  1742.     // a line for every rule (even if one does not exist, there's a comment).  It also
  1743.     // assumes that the rule number corresponds to the index into the rules array.
  1744.     // the safest way is to map the rule conditions to the var/sets and calculate the
  1745.     // rule index from that
  1746.  
  1747.     // this assumes that the sets involved in the rules are in the same order as
  1748.     // the varaibles are declared.  Since there is no restriction that set names
  1749.     // must be unique for the whole system we need to make this assumption, short 
  1750.     // of using some dot notation (var1.set1) which I don't believe is supported
  1751.     // by the IEC standard
  1752.  
  1753.     // we have a nice happy little situation here... we don't care about any of the
  1754.     // conditions for the rules!  all we care about is the rule # which is the index in
  1755.     // the rules array, and the result which we need to map to the index of the output set.
  1756.             
  1757.     char line[500];
  1758.  
  1759.     // create array of output sets...
  1760.     FuzzyVariableBase* var = get_var(OUTPUT_IDX);
  1761.  
  1762.     int num_sets = 0;
  1763.  
  1764.     if (var)
  1765.         num_sets = var->get_num_of_sets();
  1766.  
  1767.     // create an array of arrays that holds the set IDs for each variable (including output)
  1768.     std::string** sets = new std::string*[input_var_count + 1]; // add one for output var
  1769.  
  1770.     int i, j;        // counter 
  1771.  
  1772.     // loop through the input vars
  1773.     for (i = 0; i < input_var_count; i++)
  1774.         {
  1775.         var = get_var(i);
  1776.  
  1777.         num_sets = get_num_of_sets(i);
  1778.  
  1779.         // create an array of strings that has 'num_sets' elements
  1780.         sets[i] = new std::string[num_sets];
  1781.  
  1782.         for (j = 0; j < num_sets; j++)
  1783.             {
  1784.             // convert the set's ID to ascii and replace any spaces with underscores
  1785.              char* aid = convert_to_ascii(var->get_id(j), '_');
  1786.  
  1787.             // add it to the array
  1788.             sets[i][j] = aid;            
  1789.  
  1790.             delete[] aid;
  1791.  
  1792.             } // end loop through sets
  1793.  
  1794.         } // end loop through input vars
  1795.  
  1796.     // add the output variable's sets...
  1797.     num_sets = get_num_of_sets(OUTPUT_IDX);
  1798.     sets[i] = new std::string[num_sets];
  1799.  
  1800.     var = get_var(OUTPUT_IDX);
  1801.  
  1802.     for (j = 0; j < num_sets; j++)
  1803.         {
  1804.          char* aid = convert_to_ascii(var->get_id(j), '_');
  1805.  
  1806.         sets[i][j] = aid;            
  1807.  
  1808.         delete[] aid;
  1809.         } // end loop through output sets
  1810.  
  1811.     std::string tmp; // temp string to hold tokens
  1812.     // read until we get to the start of the rules declaration
  1813.  
  1814.     do
  1815.         {
  1816.         file_contents >> tmp;
  1817.         } while (strcmp(tmp.data(), "RULEBLOCK") != 0 );
  1818.  
  1819.     file_contents >> tmp; // "eat" the rule block name which isn't significant
  1820.  
  1821.     // read in the operator definition (brain-dead as we are, assume it's there!)
  1822.     // we read in the whole next line - ASSUMING it's the operator
  1823.  
  1824.     line[0] = '\0';
  1825.     // skip blank lines
  1826.     while (strlen(line) <= 0)
  1827.         file_contents.getline(line, 50);
  1828.  
  1829.     // parse it...
  1830.      char seps[]   = " :;\t\n";
  1831.  
  1832.     // right now we only care about the first part (before the colon)...
  1833.     char* operation = strtok(line, seps);
  1834.  
  1835.     // right now we only support 'and'
  1836.     if (strncmp(operation, "AND", strlen("AND")) == 0)
  1837.         {
  1838.  
  1839.         }
  1840.     else
  1841.         {
  1842.         set_msg_text(ERR_INVALID_FILE_FORMAT);
  1843.         return -1;
  1844.         }
  1845.  
  1846.     // read in the ACCUM method...
  1847.  
  1848.     line[0] = '\0';
  1849.     // skip blank lines
  1850.     while (strlen(line) <= 0)
  1851.         file_contents.getline(line, 50);
  1852.  
  1853.     char* accum = strtok(line, seps);
  1854.  
  1855.     // sanity check
  1856.     if (strncmp(accum, "ACCUM", strlen("ACCUM")) != 0)
  1857.         {
  1858.         set_msg_text(ERR_INVALID_FILE_FORMAT);
  1859.         return -1;
  1860.         }
  1861.  
  1862.     accum = strtok(NULL, seps); 
  1863.  
  1864.     // get the accumm method
  1865.  
  1866.     if (strncmp(accum, "BSUM", strlen("BSUM")) == 0)
  1867.         {
  1868.         set_composition_method(FuzzyOutVariable::COMPOSITION_OPERATION::MIN);
  1869.         }
  1870.     else if (strncmp(accum, "MAX", strlen("MAX")) == 0)
  1871.         {
  1872.         set_composition_method(FuzzyOutVariable::COMPOSITION_OPERATION::MAX);
  1873.         }
  1874.     else
  1875.         {
  1876.         set_msg_text(ERR_INVALID_FILE_FORMAT);
  1877.         return -1;
  1878.         }
  1879.  
  1880.     // now create the rules, the rule array was allocated when sets were added
  1881.     // we want to avoid the line-by-line method cuz rules can span more than one line
  1882.  
  1883.     std::string* rule_components = new std::string[input_var_count + 1]; // +1 for output var
  1884.  
  1885.     file_contents >> tmp;
  1886.  
  1887.     while (strcmp(tmp.data(), "END_RULEBLOCK") != 0)
  1888.         {
  1889.         if (file_contents.eof())
  1890.             {
  1891.             set_msg_text(ERR_EOF_READING_RULES);
  1892.              return -1;
  1893.             }
  1894.  
  1895.         // check if comment
  1896.  
  1897.         if (strncmp(tmp.data(), "(*", sizeof("(*")) == 0)
  1898.             {
  1899.             // found a comment
  1900.             // skip the rest of the line. Note, as this is a brain-dead parser
  1901.             // it assumes no multi-line comments!
  1902.             file_contents.getline(line, 500);
  1903.             } // end if found a comment
  1904.  
  1905.         if (strcmp(tmp.data(), "RULE") == 0)
  1906.             {
  1907.             int component_idx = 0;    // which component we're dealing with
  1908.             // found a rule
  1909.  
  1910.             // get the rule components...
  1911.             // look for the first condition, after the "IF"
  1912.             do
  1913.                 {
  1914.                 file_contents >> tmp;
  1915.                 } while (strcmp(tmp.data(), "IF") != 0);
  1916.  
  1917.              file_contents >> rule_components[component_idx++];  // next condition
  1918.  
  1919.             // loop through the rest until we find THEN
  1920.             // NOTE that all these comparisons are case SENSITIVE!
  1921.             file_contents >> tmp;  // next token (could be "AND" or "THEN"
  1922.             while (strcmp(tmp.data(), "THEN") != 0)
  1923.                 {
  1924.                 file_contents >> rule_components[component_idx++];  // next condition
  1925.                 file_contents >> tmp; // next token (could be "AND" or "THEN"
  1926.                 } ;
  1927.  
  1928.             // found THEN so next token is the result
  1929.  
  1930.             file_contents >> rule_components[component_idx];
  1931.             // make sure the ';' isn't included
  1932.             int semi_pos = rule_components[component_idx].find(";");
  1933.  
  1934.             if (semi_pos > 0)    // NOTE: we only check > 0 rather than >= cuz if it's = 0 we have nothing
  1935.                 (rule_components[component_idx])[semi_pos] = '\0';
  1936.  
  1937.             // calc the rule index...
  1938.  
  1939.             int rule_idx = 0;
  1940.  
  1941.             for (i = 0; i < input_var_count; i++)
  1942.                 {
  1943.                 // find match between rule_component and set saved
  1944.                 num_sets = get_num_of_sets(i);
  1945.                 for (j = 0; j < num_sets; j++)
  1946.                     {
  1947.                     if (strcmp((sets[i][j]).data(), rule_components[i].data()) == 0)
  1948.                         {
  1949.                         rule_idx +=  get_rule_index(i, j);
  1950.                         break;
  1951.                         }
  1952.                     } // end loop through sets
  1953.                 } // end loop through input vars
  1954.  
  1955.             // find the output idx (NOTE: we're using the 'i' counter above)
  1956.             int out_set_idx = NO_RULE;
  1957.             num_sets = get_num_of_sets(OUTPUT_IDX);
  1958.             for (j = 0; j < num_sets; j++)
  1959.                 {
  1960.                 if (strcmp((sets[i][j]).data(), rule_components[i].data()) == 0)
  1961.                     {
  1962.                     out_set_idx = j;
  1963.                     break;
  1964.                     }
  1965.  
  1966.                 } // end loop through sets
  1967.  
  1968.             // set the rule index
  1969.             add_rule(rule_idx,  out_set_idx);
  1970.             } // end if found a rule
  1971.     
  1972.         file_contents >> tmp;
  1973.  
  1974.         }; // end while not end ruleblock
  1975.  
  1976.     // free the memory we allocated
  1977.  
  1978.     for (i = 0; i <= input_var_count; i++) // include output var
  1979.         {
  1980.         delete[] sets[i];
  1981.         }
  1982.         
  1983.     delete[] sets;
  1984.     delete[] rule_components;
  1985.     
  1986.     return 0;
  1987.  
  1988. } // end FuzzyModelBase::load_rules_from_file()
  1989.  
  1990.  
  1991. //
  1992. // Function:    load_defuzz_block_from_fcl_file()
  1993. // 
  1994. // Purpose:        Loads the defuzzification info from the FCL file and creates them.
  1995. //
  1996. // Arguments:
  1997. //
  1998. //        std::ifstream&    file_contnts    - file to read from
  1999. //
  2000. // Returns:
  2001. //
  2002. //        0 - success
  2003. //        non-zero - failure
  2004. //
  2005. // Author:    Michael Zarozinski
  2006. // Date:    9/01
  2007. // 
  2008. // Modification History
  2009. // Author    Date        Modification
  2010. // ------    ----        ------------
  2011. //
  2012. //    
  2013.  
  2014. int FuzzyModelBase::load_defuzz_block_from_fcl_file(std::ifstream& file_contents)
  2015. {
  2016.     int method;                // defuzzification method
  2017.      file_contents.seekg(0);    // go to start of file
  2018.  
  2019.       std::string token;
  2020.  
  2021.     // loop until we find the "DEFUZZIFY" keyword
  2022.     do 
  2023.         {
  2024.         file_contents >> token;
  2025.  
  2026.         if (file_contents.eof())
  2027.             {
  2028.             // didn't find a defuzzify block... set defaults
  2029.              set_defuzz_method(DefuzzVarObj::DEFUZZ_TYPE::COG);
  2030.              return 0;
  2031.             }
  2032.  
  2033.         }while (strcmp(token.data(), "DEFUZZIFY") != 0);
  2034.  
  2035.     // eat the next token which is the name of the ouptut variable
  2036.     file_contents >> token;
  2037.  
  2038.     // now search for 'METHOD'...
  2039.     do 
  2040.         {
  2041.         file_contents >> token;
  2042.  
  2043.         // if token is empty we reached EOF
  2044.         if (file_contents.eof())
  2045.             {
  2046.             // didn't find the "METHOD" keyword... set defaults
  2047.              set_defuzz_method(DefuzzVarObj::DEFUZZ_TYPE::COG);
  2048.             return 0;
  2049.             }
  2050.  
  2051.         }while ( strcmp(token.data(), "METHOD:") != 0) ;
  2052.  
  2053.     // now get the method...
  2054.  
  2055.     file_contents >> token;
  2056.  
  2057.     // strip off the semicolon (if it's there)
  2058.     int semi_pos = token.find(";");
  2059.  
  2060.     if (semi_pos > 0)    // NOTE: we only check > 0 rather than >= cuz if it's = 0 we have nothing
  2061.         token[semi_pos] = '\0';
  2062.  
  2063.     // find the defuzz method
  2064.     // defuzzification_method ::= 'METHOD' ':' 'CoG' | 'CoGS' | 'CoA' | 'LM' | 'RM' | 'MoM' ';'
  2065.     // *** NOTE: 'MoM' (Mean of Maximum) is not part of the standard, we added it ***
  2066.     // AND we only currently support "CoG" and "MoM"
  2067.  
  2068.     if (strcmp(token.data(), "CoG") == 0)
  2069.         method = DefuzzVarObj::DEFUZZ_TYPE::COG;
  2070.     else if (strcmp(token.data(), "MoM") == 0)
  2071.         method = DefuzzVarObj::DEFUZZ_TYPE::MOM;
  2072.     else
  2073.         {
  2074.         // default to Center of Gravity
  2075.         method = DefuzzVarObj::DEFUZZ_TYPE::COG;
  2076.         }
  2077.      
  2078.     set_defuzz_method(method);
  2079.  
  2080.     return 0;
  2081.  
  2082. } // end FuzzyModelBase::load_defuzz_block_from_file()
  2083.  
  2084.  
  2085. //
  2086. // Function:    set_output_dom()
  2087. // 
  2088. // Purpose:        Set the output set dom according to the composition method
  2089. //
  2090. // Arguments:
  2091. //
  2092. //        DOMType*    out_set_dom_arr -    Array that holds the DOM value for each
  2093. //                                        set in the output variable
  2094. //        int            set_idx            -    index of the set we're setting the DOM for
  2095. //        DOMType        new_value        -    DOM value
  2096. //
  2097. // Returns:
  2098. //
  2099. //        0 - success
  2100. //        non-zero - failure
  2101. //
  2102. // Author:    Michael Zarozinski
  2103. // Date:    9/01
  2104. // 
  2105. // Modification History
  2106. // Author    Date        Modification
  2107. // ------    ----        ------------
  2108. //
  2109. //
  2110. // 
  2111.  
  2112. int FuzzyModelBase::set_output_dom(DOMType* out_set_dom_arr, int set_idx, DOMType new_value)  
  2113. {
  2114.     bool set_value = false;    // indicates if we need to set value
  2115.     int composition_method = output_var->get_composition_method();
  2116.  
  2117.     if (set_idx >= output_var->get_num_of_sets())
  2118.         {
  2119.         assert(set_idx < output_var->get_num_of_sets());
  2120.         return -1;
  2121.         }
  2122.  
  2123.     // set the value dependent on the COMPOSITION method...
  2124.     DOMType current_dom = out_set_dom_arr[set_idx];
  2125.  
  2126.     // if dom passed in is 0 or current dom is 0 update.  This is needed so we can zero it out, besides if
  2127.     // we're passing in a zero the set is NOT active so we WANT it to be zero
  2128.      if (current_dom == 0)
  2129.         set_value = true;
  2130.     else
  2131.         {
  2132.         if ((composition_method == FuzzyOutVariable::COMPOSITION_OPERATION::MIN) && ( new_value < current_dom) )
  2133.             set_value = true;
  2134.         else if ((composition_method == FuzzyOutVariable::COMPOSITION_OPERATION::MAX) && ( new_value > current_dom))
  2135.             set_value = true;
  2136.         }
  2137.  
  2138.     if (set_value)
  2139.         {
  2140.         if (new_value != 255 && new_value > FuzzyVariableBase::get_dom_array_max_idx())
  2141.             assert(0);
  2142.  
  2143.         if (new_value < 0)
  2144.             new_value = 0;
  2145.  
  2146.         out_set_dom_arr[set_idx] = new_value; 
  2147.  
  2148.         } // end if we need to set the DOM
  2149.                  
  2150.     return 0;
  2151.  
  2152. } // end FuzzyModelBase::set_output_dom()
  2153.  
  2154.  
  2155. /////////////////////////////////////////////////////////////////////
  2156. ////////// Trivial Functions That Don't Require Headers /////////////
  2157. /////////////////////////////////////////////////////////////////////
  2158.  
  2159. FFLL_INLINE int FuzzyModelBase::get_input_var_count(void) const
  2160. {
  2161.     return input_var_count;
  2162.  
  2163. }  // end FuzzyModelBase::get_input_var_count()
  2164.  
  2165. FFLL_INLINE int FuzzyModelBase::get_total_var_count() const
  2166. {
  2167.     int count = get_input_var_count();
  2168.  
  2169.     if (output_var)
  2170.         count++;
  2171.  
  2172.     return count;
  2173.  
  2174. }; // end FuzzyModelBase::get_total_var_count()
  2175.  
  2176. FFLL_INLINE int FuzzyModelBase::get_num_of_rules() const
  2177. {
  2178.     if (rules)
  2179.         return rules->get_max();
  2180.     else
  2181.         return 0;
  2182.  
  2183. }; // end FuzzyModelBase::get_num_of_rules()
  2184.  
  2185.  
  2186. int FuzzyModelBase::get_rule_index(int _var_idx, int _set_idx /* = -1 */) const
  2187. {
  2188.     // if _set_id is not passed - return the index for the VAR - this
  2189.     // is used to reverse engineer the rule components from an index into the rule array
  2190.     if (_set_idx == -1)
  2191.         {
  2192.         // if last var, return 1... we're done recursing
  2193.         if (_var_idx == input_var_count - 1)
  2194.             return 1;
  2195.  
  2196.         return input_var_arr[_var_idx]->get_rule_index();
  2197.         } 
  2198.     else
  2199.         {
  2200.         return input_var_arr[_var_idx]->get_rule_index(_set_idx);
  2201.         }
  2202.  
  2203. }; // end FuzzyModelBase::get_rule_index()
  2204.  
  2205. int FuzzyModelBase::calc_num_of_rules() const 
  2206. {
  2207.     int num_of_rules = 1; // init to 1 so we don't mult by 0
  2208.  
  2209.     for (int i = 0; i < input_var_count; i++)
  2210.         {
  2211.         // multiply by 0 is ok, cuz if one variable has no
  2212.         // sets we can't have any rules.
  2213.          num_of_rules *= input_var_arr[i]->get_num_of_sets();
  2214.         } // end loop through sets
  2215.  
  2216.     return num_of_rules;
  2217.  
  2218. } // end FuzzyModelBase::calc_num_of_rules()
  2219.  
  2220. FFLL_INLINE int FuzzyModelBase::get_num_of_sets(int var_idx) const
  2221. {
  2222.  
  2223.     const FuzzyVariableBase* var =  get_var(var_idx);
  2224.  
  2225.     assert(var != NULL);
  2226.  
  2227.     return var->get_num_of_sets();
  2228.  
  2229. }; // end FuzzyModelBase::get_num_of_sets()
  2230.  
  2231.  
  2232. void FuzzyModelBase::add_rule(int index, RuleArrayType output_set)
  2233. {
  2234.     rules->add_rule(index, output_set); 
  2235. };
  2236.  
  2237. void FuzzyModelBase::remove_rule(int index)
  2238. {    
  2239.     rules->remove_rule(index); 
  2240. };
  2241.  
  2242.  
  2243. void FuzzyModelBase::init()
  2244. {
  2245.     if (rules)
  2246.         delete rules;
  2247.  
  2248.     rules =  new_rule_array();
  2249. };
  2250.  
  2251. RuleArray* FuzzyModelBase::new_rule_array()
  2252. {
  2253.     return new RuleArray(this);
  2254. };  
  2255.  
  2256. FFLL_INLINE RuleArrayType FuzzyModelBase::rule_exists(int index) const 
  2257. {
  2258.     return (((rules->get_rule(index) == NO_RULE) ? 0 : 1));
  2259. };  
  2260.  
  2261. FFLL_INLINE RuleArrayType FuzzyModelBase::get_rule(int idx) const  
  2262. {
  2263.     return rules->get_rule(idx);
  2264. };
  2265.  
  2266. FFLL_INLINE bool FuzzyModelBase::no_rules() const
  2267.     return rules->no_rules();
  2268. };
  2269.  
  2270. FFLL_INLINE void FuzzyModelBase::clear_rules() 
  2271.     rules->clear();
  2272. };
  2273.  
  2274. FFLL_INLINE    const char* FuzzyModelBase::get_model_name() const
  2275. {
  2276.     return model_name.data();
  2277. }; 
  2278. const char* FuzzyModelBase::get_fcl_block_start() const 
  2279. {
  2280.     return "FUNCTION_BLOCK";
  2281. };        
  2282.  
  2283. const char* FuzzyModelBase::get_fcl_block_end() const 
  2284. {
  2285.     return "END_FUNCTION_BLOCK"; 
  2286. };
  2287. void FuzzyModelBase::set_model_name(const char* _name) 
  2288. {
  2289.     model_name = _name;
  2290. };
  2291.  
  2292. DOMType FuzzyModelBase::get_dom(int var_idx, int set_idx, int x_position) const
  2293. {
  2294.     const FuzzyVariableBase* var = get_var(var_idx);
  2295.      return var->get_dom(set_idx, x_position);
  2296.  
  2297. FuzzyVariableBase* FuzzyModelBase::new_variable()
  2298. {
  2299.      return new FuzzyVariableBase(this);
  2300. }
  2301.  
  2302. FuzzyOutVariable* FuzzyModelBase::new_output_variable()
  2303. {
  2304.     return new FuzzyOutVariable(this);
  2305. }  
  2306.  
  2307. ValuesArrCountType FuzzyModelBase::convert_value_to_idx(int var_idx, RealType value) const
  2308. {
  2309.  
  2310.     FuzzyVariableBase* var =  get_var(var_idx);
  2311.     return var->convert_value_to_idx(value);
  2312.  
  2313. }  
  2314.  
  2315. int FuzzyModelBase::get_defuzz_method() const
  2316. {
  2317.     if (output_var)
  2318.         return output_var->get_defuzz_method();
  2319.     else
  2320.         return -1;
  2321.  
  2322. }  
  2323.  
  2324.  
  2325. int FuzzyModelBase::get_composition_method() const
  2326. {
  2327.     if (!output_var)
  2328.         return -1;
  2329.  
  2330.     return output_var->get_composition_method();
  2331.  
  2332. int FuzzyModelBase::set_composition_method(int method)
  2333. {
  2334.     if (!output_var)
  2335.         return -1;
  2336.  
  2337.     return output_var->set_composition_method(method);
  2338.  
  2339.  
  2340.         
  2341. int FuzzyModelBase::set_defuzz_method(int method) 
  2342. {
  2343.     if (!output_var)
  2344.         return -1;
  2345.     return output_var->set_defuzz_method(method);
  2346. };
  2347.  
  2348. FFLL_INLINE int FuzzyModelBase::get_inference_method() const 
  2349. {
  2350.     return inference_method; 
  2351.  
  2352. int FuzzyModelBase::set_inference_method(int method)
  2353. {
  2354.     // validate
  2355.  
  2356.     switch (method)
  2357.         {
  2358.         case INFERENCE_OPERATION::MIN:
  2359.         case INFERENCE_OPERATION::MAX:
  2360.  
  2361.             inference_method = method;
  2362.             break;
  2363.  
  2364.         default:
  2365.             set_msg_text(ERR_INVALID_INFERENCE_MTHD);
  2366.             return -1;    // invalid method  
  2367.     
  2368.         } // end switch on method
  2369.  
  2370.     return 0;
  2371.  
  2372. } // end FuzzyModelBase::set_inference_method
  2373.  
  2374. FuzzyVariableBase* FuzzyModelBase::get_var(int idx) const
  2375. {
  2376.     if (idx == OUTPUT_IDX) // output var
  2377.         return output_var;
  2378.     else
  2379.         return(((input_var_count == 0) || (idx > input_var_count)) ? NULL : input_var_arr[idx]);
  2380. };
  2381.