tclpkg-dlist.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 1998-2004 California Institute of Technology
00006 // Copyright (c) 2004-2007 University of Southern California
00007 // Rob Peters <rjpeters at usc dot edu>
00008 //
00009 // created: Tue Dec  1 08:00:00 1998
00010 // commit: $Id: tclpkg-dlist.cc 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/tcl/tclpkg-dlist.cc $
00012 //
00013 // --------------------------------------------------------------------
00014 //
00015 // This file is part of GroovX.
00016 //   [http://ilab.usc.edu/rjpeters/groovx/]
00017 //
00018 // GroovX is free software; you can redistribute it and/or modify it
00019 // under the terms of the GNU General Public License as published by
00020 // the Free Software Foundation; either version 2 of the License, or
00021 // (at your option) any later version.
00022 //
00023 // GroovX is distributed in the hope that it will be useful, but
00024 // WITHOUT ANY WARRANTY; without even the implied warranty of
00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00026 // General Public License for more details.
00027 //
00028 // You should have received a copy of the GNU General Public License
00029 // along with GroovX; if not, write to the Free Software Foundation,
00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00031 //
00033 
00034 #ifndef GROOVX_TCL_TCLPKG_DLIST_CC_UTC20050628161246_DEFINED
00035 #define GROOVX_TCL_TCLPKG_DLIST_CC_UTC20050628161246_DEFINED
00036 
00037 #include "tcl/tclpkg-dlist.h"
00038 
00039 #include "tcl/tclpkg-dlist.h"
00040 
00041 // This file implements additional Tcl list manipulation functions
00042 
00043 #include "tcl/list.h"
00044 #include "tcl/pkg.h"
00045 
00046 #include "rutz/algo.h"
00047 #include "rutz/arrays.h"
00048 #include "rutz/error.h"
00049 #include "rutz/rand.h"
00050 
00051 #include "rutz/trace.h"
00052 #include "rutz/debug.h"
00053 GVX_DBG_REGISTER
00054 
00055 #include <algorithm> // for std::random_shuffle
00056 #include <cmath>
00057 
00058 // Helper functions
00059 namespace
00060 {
00061   template <class Itr>
00062   double perm_distance_aux(Itr itr, Itr end)
00063   {
00064     int c = 0;
00065     double result = 0.0;
00066     while (itr != end)
00067       {
00068         result += fabs(double(*itr) - double(c));
00069         ++itr;
00070         ++c;
00071       }
00072 
00073     return result / double(c);
00074   }
00075 
00076   template <class Itr>
00077   double perm_distance2_aux(Itr itr, Itr end, double power)
00078   {
00079     int c = 0;
00080     double result = 0.0;
00081     while (itr != end)
00082       {
00083         result += pow(fabs(double(*itr) - double(c)), power);
00084         ++itr;
00085         ++c;
00086       }
00087 
00088     return pow(result, 1.0/power) / double(c);
00089   }
00090 }
00091 
00092 namespace Dlist
00093 {
00094 
00095   //---------------------------------------------------------
00096   //
00097   // This command takes two lists as arguments, and uses the integers in
00098   // the second (index) list to return a permutation of the elements in
00099   // the first (source) list
00100   //
00101   // Example:
00102   //      dlist_choose { 3 5 7 } { 2 0 1 }
00103   // returns
00104   //      7 3 5
00105   //
00106   //---------------------------------------------------------
00107 
00108   tcl::list choose(tcl::list source_list, tcl::list index_list)
00109   {
00110     tcl::list result;
00111 
00112     for (unsigned int i = 0; i < index_list.length(); ++i)
00113       {
00114         unsigned int index = index_list.get<unsigned int>(i);
00115 
00116         // use that int as an index into source list, getting the
00117         // corresponding list element and appending it to the output list
00118         result.append(source_list.at(index));
00119       }
00120 
00121     GVX_ASSERT(result.length() == index_list.length());
00122 
00123     return result;
00124   }
00125 
00126   //---------------------------------------------------------
00127   //
00128   // Cyclically shift the elements of the list leftward by n steps.
00129   //
00130   //---------------------------------------------------------
00131 
00132   tcl::list cycle_left(tcl::list source_list, unsigned int n)
00133   {
00134     n = n % source_list.length();
00135 
00136     if (n == 0)
00137       return source_list;
00138 
00139     tcl::list result;
00140 
00141     for (unsigned int i = n; i < source_list.length(); ++i)
00142       {
00143         result.append(source_list.at(i));
00144       }
00145 
00146     for (unsigned int i = 0; i < n; ++i)
00147       {
00148         result.append(source_list.at(i));
00149       }
00150 
00151     return result;
00152   }
00153 
00154   //---------------------------------------------------------
00155   //
00156   // Cyclically shift the elements of the list rightward by n steps.
00157   //
00158   //---------------------------------------------------------
00159 
00160   tcl::list cycle_right(tcl::list source_list, unsigned int n)
00161   {
00162     n = n % source_list.length();
00163 
00164     if (n == 0)
00165       return source_list;
00166 
00167     return cycle_left(source_list, source_list.length() - n);
00168   }
00169 
00170   //---------------------------------------------------------
00171   //
00172   // Returns the n'th element of the list; generates an error if n is
00173   // out of range.
00174   //
00175   //---------------------------------------------------------
00176 
00177   tcl::obj index(tcl::list source_list, unsigned int n)
00178   {
00179     return source_list.at(n);
00180   }
00181 
00182   //---------------------------------------------------------
00183   //
00184   // This command takes as its argument a single list containing only
00185   // integers, and returns a list in which each element is the logical
00186   // negation of its corresponding element in the source list.
00187   //
00188   //---------------------------------------------------------
00189 
00190   tcl::list not_(tcl::list source_list)
00191   {
00192     tcl::obj one = tcl::convert_from<int>(1);
00193     tcl::obj zero = tcl::convert_from<int>(0);
00194     tcl::list result;
00195 
00196     for (unsigned int i = 0; i < source_list.length(); ++i)
00197       {
00198         if ( source_list.get<int>(i) == 0 )
00199           result.append(one);
00200         else
00201           result.append(zero);
00202       }
00203 
00204     GVX_ASSERT(result.length() == source_list.length());
00205 
00206     return result;
00207   }
00208 
00209   //---------------------------------------------------------
00210   //
00211   // this command produces a list of ones of the length specified by its
00212   // lone argument
00213   //
00214   //---------------------------------------------------------
00215 
00216   tcl::list ones(unsigned int num_ones)
00217   {
00218     tcl::list result;
00219     result.append(1, num_ones);
00220 
00221     return result;
00222   }
00223 
00224   //---------------------------------------------------------
00225   //
00226   // This commmand returns a single element chosen at random
00227   // from the source list
00228   //
00229   //---------------------------------------------------------
00230 
00231   tcl::obj pickone(tcl::list source_list)
00232   {
00233     if (source_list.length() == 0)
00234       {
00235         throw rutz::error("source_list is empty", SRC_POS);
00236       }
00237 
00238     return source_list.at(rutz::rand_range(0u, source_list.length()));
00239   }
00240 
00241   //---------------------------------------------------------
00242   //
00243   // this command produces an ordered list of all integers between begin
00244   // and end, inclusive.
00245   //
00246   //---------------------------------------------------------
00247 
00248   tcl::list range(int begin, int end, int step)
00249   {
00250     tcl::list result;
00251 
00252     if (step == 0)
00253       {
00254         // special case: if step is 0, we return an empty list
00255       }
00256     else if (step > 0)
00257       {
00258         for (int i = begin; i <= end; i += step)
00259           {
00260             result.append(i);
00261           }
00262       }
00263     else // step < 0
00264       {
00265         for (int i = begin; i >= end; i += step)
00266           {
00267             result.append(i);
00268           }
00269       }
00270 
00271     return result;
00272   }
00273 
00274   //---------------------------------------------------------
00275   //
00276   // Make a series of linearly spaced values between (and including) two
00277   // endpoints
00278   //
00279   //---------------------------------------------------------
00280 
00281   tcl::list linspace(double begin, double end, unsigned int npts)
00282   {
00283     tcl::list result;
00284 
00285     if (npts < 2)
00286       {
00287         throw rutz::error("npts must be at least 2", SRC_POS);
00288       }
00289 
00290     const double skip = (end - begin) / (npts - 1);
00291 
00292     bool integer_mode = (skip == int(skip) && begin == int(begin));
00293 
00294     if (integer_mode)
00295       for (unsigned int i = 0; i < npts; ++i)
00296         {
00297           result.append(int(begin + i*skip));
00298         }
00299     else
00300       for (unsigned int i = 0; i < npts; ++i)
00301         {
00302           result.append(begin + i*skip);
00303         }
00304 
00305     return result;
00306   }
00307 
00308   double perm_distance(tcl::list src)
00309   {
00310     return perm_distance_aux(src.begin<unsigned int>(),
00311                              src.end<unsigned int>());
00312   }
00313 
00314   double perm_distance2(tcl::list src, double power)
00315   {
00316     return perm_distance2_aux(src.begin<unsigned int>(),
00317                               src.end<unsigned int>(),
00318                               power);
00319   }
00320 
00321   //---------------------------------------------------------
00322   //
00323   // generate a complete/pure permutation of the numbers 0..N-1
00324   // the result is such that:
00325   //   result[i] != i         for all i
00326   //   sum(abs(result[i]-i))  is maximal
00327   //
00328   // WARNING: At first glance this might sound like it yields a nice random
00329   // list, but in fact simply reversing the order of elements gives a
00330   // result that satisfies the constraints of this algorithm, without being
00331   // random at all!
00332   //
00333   //---------------------------------------------------------
00334 
00335   tcl::list permute_maximal(unsigned int N)
00336   {
00337     if (N < 2)
00338       throw rutz::error("N must be at least 2 to make a permutation",
00339                         SRC_POS);
00340 
00341     double maxdist = double(N)/2.0;
00342 
00343     if (N%2)
00344       {
00345         const double half = double(N)/2.0;
00346         maxdist = half + 1.0/(2.0 + 1.0/half);
00347       }
00348 
00349     maxdist -= 0.0001;
00350 
00351     rutz::fixed_block<unsigned int> slots(N);
00352 
00353     for (unsigned int i = 0; i < slots.size()-1; ++i)
00354       slots[i] = i+1;
00355 
00356     slots[slots.size()-1] = 0;
00357 
00358     double dist = perm_distance_aux(slots.begin(), slots.end());
00359 
00360     for (int c = 0; c < 100000; ++c)
00361       {
00362         unsigned int i = rutz::rand_range(0u, N);
00363         unsigned int j = i;
00364         while (j == i)
00365           {
00366             j = rutz::rand_range(0u, N);
00367           }
00368 
00369         if (slots[j] != i && slots[i] != j)
00370           {
00371             const double origdist =
00372               fabs(double(i)-double(slots[i])) +
00373               fabs(double(j)-double(slots[j]));
00374 
00375             const double newdist =
00376               fabs(double(j)-double(slots[i])) +
00377               fabs(double(i)-double(slots[j]));
00378 
00379             if (newdist > origdist)
00380               {
00381                 rutz::swap2(slots[i], slots[j]);
00382                 dist += (newdist-origdist)/double(N);
00383               }
00384           }
00385 
00386         if (dist >= maxdist)
00387           {
00388             double distcheck = perm_distance_aux(slots.begin(), slots.end());
00389             if (distcheck < maxdist)
00390               {
00391                 throw rutz::error("snafu in permutation "
00392                                   "distance computation", SRC_POS);
00393               }
00394 
00395             dbg_eval_nl(3, c);
00396 
00397             tcl::list result;
00398 
00399             for (unsigned int i = 0; i < slots.size(); ++i)
00400               {
00401                 result.append(slots[i]);
00402               }
00403 
00404             return result;
00405           }
00406       }
00407 
00408     throw rutz::error("permutation algorithm failed to converge",
00409                       SRC_POS);
00410     return tcl::list(); // can't happen, but placate compiler
00411   }
00412 
00413   //---------------------------------------------------------
00414   //
00415   // generate a random permutation of the numbers 0..N-1 such that:
00416   //   result[i] != i         for all i
00417   //
00418   //---------------------------------------------------------
00419 
00420   tcl::list permute_moveall(unsigned int N)
00421   {
00422     if (N < 2)
00423       throw rutz::error("N must be at least 2 to make a permutation", SRC_POS);
00424 
00425     rutz::fixed_block<bool> used(N);
00426     for (unsigned int i = 0; i < N; ++i)
00427       used[i] = false;
00428 
00429     rutz::fixed_block<unsigned int> slots(N);
00430 
00431     // fill slots[0] ... slots[N-2]
00432     for (unsigned int i = 0; i < N-1; ++i)
00433       {
00434         unsigned int v = i;
00435         while (v == i || used[v])
00436           v = rutz::rand_range(0u, N);
00437 
00438         GVX_ASSERT(v < N);
00439 
00440         used[v] = true;
00441         slots[i] = v;
00442       }
00443 
00444     // figure out which is the last available slot
00445     unsigned int lastslot = N;
00446     for (unsigned int i = 0; i < N; ++i)
00447       if (!used[i])
00448         {
00449           lastslot = i;
00450           break;
00451         }
00452 
00453     GVX_ASSERT(lastslot != N);
00454 
00455     if (lastslot == N)
00456       {
00457         slots[N-1] = slots[N-2];
00458         slots[N-2] = lastslot;
00459       }
00460     else
00461       {
00462         slots[N-1] = lastslot;
00463       }
00464 
00465     tcl::list result;
00466 
00467     for (unsigned int i = 0; i < slots.size(); ++i)
00468       {
00469         result.append(slots[i]);
00470       }
00471 
00472     return result;
00473   }
00474 
00475   //---------------------------------------------------------
00476   //
00477   // this command produces a list of random numbers each between min and
00478   // max, and of the given
00479   //
00480   //---------------------------------------------------------
00481 
00482   tcl::list rand(double min, double max, unsigned int N)
00483   {
00484     tcl::list result;
00485 
00486     static rutz::urand generator;
00487 
00488     for (unsigned int i = 0; i < N; ++i)
00489       {
00490         result.append(generator.fdraw_range(min, max));
00491       }
00492 
00493     return result;
00494   }
00495 
00496   //---------------------------------------------------------
00497   //
00498   // This command taks two lists as arguments. Each element from the
00499   // first (source) list is appended to the result multiple times; the
00500   // number of times is determined by the corresponding integer found in
00501   // the second (times) list.
00502   //
00503   // For example:
00504   //      dlist_repeat { 4 5 6 } { 1 2 3 }
00505   // returns
00506   //      4 5 5 6 6 6
00507   //
00508   //---------------------------------------------------------
00509 
00510   tcl::list repeat(tcl::list source_list, tcl::list times_list)
00511   {
00512     // find the minimum of the two lists' lengths
00513     unsigned int min_len = rutz::min(source_list.length(), times_list.length());
00514 
00515     tcl::list result;
00516 
00517     for (unsigned int t = 0; t < min_len; ++t)
00518       {
00519         result.append(source_list.at(t),
00520                       times_list.get<unsigned int>(t));
00521       }
00522 
00523     return result;
00524   }
00525 
00526   //---------------------------------------------------------
00527   //
00528   // Return a new list containing the elements of the source list in
00529   // reverse order.
00530   //
00531   //---------------------------------------------------------
00532 
00533   tcl::list reverse(tcl::list src)
00534   {
00535     if (src.length() < 2)
00536       return src;
00537 
00538     tcl::list result;
00539     for (unsigned int i = 0; i < src.length(); ++i)
00540       result.append(src.at(src.length()-i-1));
00541     return result;
00542   }
00543 
00544   //---------------------------------------------------------
00545   //
00546   // This command takes two lists as arguments, using the binary flags
00547   // in the second (flags) list to choose which elements from the first
00548   // (source) list should be appended to the output list
00549   //
00550   //---------------------------------------------------------
00551 
00552   tcl::list select(tcl::list source_list, tcl::list flags_list)
00553   {
00554     unsigned int src_len = source_list.length();
00555     unsigned int flg_len = flags_list.length();
00556 
00557     if (flg_len < src_len)
00558       {
00559         throw rutz::error("flags list must be as long as source_list",
00560                           SRC_POS);
00561       }
00562 
00563     tcl::list result;
00564 
00565     for (unsigned int i = 0; i < src_len; ++i)
00566       {
00567         // if the flag is true, add the corresponding source_list
00568         // element to the result list
00569         if ( flags_list.get<int>(i) )
00570           {
00571             result.append(source_list.at(i));
00572           }
00573       }
00574 
00575     return result;
00576   }
00577 
00578   //---------------------------------------------------------
00579   //
00580   // dlist::shuffle
00581   //
00582   //---------------------------------------------------------
00583 
00584   tcl::list shuffle(tcl::list src, int seed)
00585   {
00586     rutz::fixed_block<tcl::obj> objs(src.begin<tcl::obj>(),
00587                                         src.end<tcl::obj>());
00588 
00589     rutz::urand generator(seed);
00590 
00591     std::random_shuffle(objs.begin(), objs.end(), generator);
00592 
00593     tcl::list result;
00594 
00595     for (unsigned int i = 0; i < objs.size(); ++i)
00596       {
00597         result.append(objs[i]);
00598       }
00599 
00600     return result;
00601   }
00602 
00603   //---------------------------------------------------------
00604   //
00605   // Shuffle an input list through a random permutation such that no
00606   // element remains in its initial position.
00607   //
00608   //---------------------------------------------------------
00609 
00610   tcl::list shuffle_moveall(tcl::list src)
00611   {
00612     tcl::list permutation = permute_moveall(src.length());
00613     return Dlist::choose(src, permutation);
00614   }
00615 
00616   //---------------------------------------------------------
00617   //
00618   // Shuffle an input list through a maximal permutation.
00619   //
00620   //---------------------------------------------------------
00621 
00622   tcl::list shuffle_maximal(tcl::list src)
00623   {
00624     tcl::list permutation = permute_maximal(src.length());
00625     return Dlist::choose(src, permutation);
00626   }
00627 
00628   //---------------------------------------------------------
00629   //
00630   // dlist::slice
00631   //
00632   //---------------------------------------------------------
00633 
00634   tcl::list slice(tcl::list src, unsigned int slice)
00635   {
00636     tcl::list result;
00637 
00638     for (unsigned int i = 0, end = src.length(); i < end; ++i)
00639       {
00640         tcl::list sub(src.at(i));
00641         result.append(sub.at(slice));
00642       }
00643 
00644     GVX_ASSERT(result.length() == src.length());
00645 
00646     return result;
00647   }
00648 
00649   //---------------------------------------------------------
00650   //
00651   // this command sums the numbers in a list, trying to return an int
00652   // result if possible, but returning a double result if any doubles
00653   // are found in the source list
00654   //
00655   //---------------------------------------------------------
00656 
00657   tcl::obj sum(tcl::list source_list)
00658   {
00659     int isum=0;
00660     double dsum=0.0;
00661     bool seen_double=false;
00662 
00663     for (unsigned int i = 0; i < source_list.length(); /* incr in loop body*/)
00664       {
00665         if ( !seen_double )
00666           {
00667             try
00668               {
00669                 isum += source_list.get<int>(i);
00670               }
00671             catch(rutz::error&)
00672               {
00673                 seen_double = true;
00674                 dsum = isum;
00675                 continue; // skip the increment
00676               }
00677           }
00678         else
00679           {
00680             dsum += source_list.get<double>(i);
00681           }
00682 
00683         ++i; // here's the increment
00684       }
00685 
00686     if ( !seen_double )
00687       return tcl::convert_from<int>(isum);
00688     else
00689       return tcl::convert_from<double>(dsum);
00690   }
00691 
00692   //---------------------------------------------------------
00693   //
00694   // this command produces a list of zeros of the length specified by its
00695   // lone argument
00696   //
00697   //---------------------------------------------------------
00698 
00699   tcl::list zeros(unsigned int num_zeros)
00700   {
00701     tcl::list result;
00702     result.append(0, num_zeros);
00703 
00704     return result;
00705   }
00706 
00707 } // end namespace Dlist
00708 
00709 
00710 extern "C"
00711 int Dlist_Init(Tcl_Interp* interp)
00712 {
00713 GVX_TRACE("Dlist_Init");
00714 
00715   GVX_PKG_CREATE(pkg, interp, "dlist", "4.$Revision: 10065 $");
00716 
00717   pkg->def( "choose", "source_list index_list", &Dlist::choose, SRC_POS );
00718   pkg->def( "cycle_left", "list n", &Dlist::cycle_left, SRC_POS );
00719   pkg->def( "cycle_right", "list n", &Dlist::cycle_right, SRC_POS );
00720   pkg->def( "index", "list index", &Dlist::index, SRC_POS );
00721   pkg->def( "not", "list", &Dlist::not_, SRC_POS );
00722   pkg->def( "ones", "num_ones", &Dlist::ones, SRC_POS );
00723   pkg->def( "linspace", "begin end npts", &Dlist::linspace, SRC_POS );
00724   pkg->def( "perm_distance", "list", &Dlist::perm_distance, SRC_POS );
00725   pkg->def( "perm_distance2", "list power", &Dlist::perm_distance2, SRC_POS );
00726   pkg->def( "permute_maximal", "N", &Dlist::permute_maximal, SRC_POS );
00727   pkg->def( "permute_moveall", "N", &Dlist::permute_moveall, SRC_POS );
00728   pkg->def( "pickone", "list", &Dlist::pickone, SRC_POS );
00729   pkg->def( "rand", "min max N", &Dlist::rand, SRC_POS );
00730   pkg->def( "range", "begin end ?step=1?", &Dlist::range, SRC_POS );
00731   pkg->def( "range", "begin end", rutz::bind_last(&Dlist::range, 1), SRC_POS );
00732   pkg->def( "repeat", "source_list times_list", &Dlist::repeat, SRC_POS );
00733   pkg->def( "reverse", "list", &Dlist::reverse, SRC_POS );
00734   pkg->def( "select", "source_list flags_list", &Dlist::select, SRC_POS );
00735   pkg->def( "shuffle", "list ?seed=0?", &Dlist::shuffle, SRC_POS );
00736   pkg->def( "shuffle", "list", rutz::bind_last(&Dlist::shuffle, 0), SRC_POS );
00737   pkg->def( "shuffle_maximal", "list", &Dlist::shuffle_maximal, SRC_POS );
00738   pkg->def( "shuffle_moveall", "list", &Dlist::shuffle_moveall, SRC_POS );
00739   pkg->def( "slice", "list n", &Dlist::slice, SRC_POS );
00740   pkg->def( "sum", "list", &Dlist::sum, SRC_POS );
00741   pkg->def( "zeros", "num_zeros", &Dlist::zeros, SRC_POS );
00742 
00743   GVX_PKG_RETURN(pkg);
00744 }
00745 
00746 static const char __attribute__((used)) vcid_groovx_tcl_tclpkg_dlist_cc_utc20050628161246[] = "$Id: tclpkg-dlist.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00747 #endif // !GROOVX_TCL_TCLPKG_DLIST_CC_UTC20050628161246_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:41 2008 by Doxygen version 1.5.5.