shell command for SmoothImage.cxx from ANTs library - c++
I have the code below which I would like to run it from a bash script in ubuntu. Inside my bash script, I wrote the command below.
$DOANTS/SmoothImage $216*512*512 $RESULTS/Registration/NiftyReg/StructureGuided/Pat28_MLAF0113_pv_Aff_BSpline_Iso_ArgMax.nii.gz $1 $RESULTS/Registration/NiftyReg/StructureGuided/Pat28_${CASES[$i]}\_Prostate_Aff_BSpline_Iso_ArgMax_smoothed.nii.gz
but I get the following error:
Unsupported dimension
I am not sure why the dimension is not passed. would be thankful if you could let me know the reason. The true size of image is. 216*512*512
The code is as below:
#include "antsUtilities.h"
#include <algorithm>
#include "itkMedianImageFilter.h"
#include "itkDiscreteGaussianImageFilter.h"
#include "ReadWriteData.h"
namespace ants
{
template <unsigned int ImageDimension>
int SmoothImage(int argc, char *`1 [])
{
typedef float PixelType;
typedef itk::Image<PixelType, ImageDimension> ImageType;
std::vector<float> sigmaVector = ConvertVector<float>( argv[3] );
typename ImageType::Pointer image1 = ITK_NULLPTR;
typename ImageType::Pointer varimage = ITK_NULLPTR;
ReadImage<ImageType>(image1, argv[2]);
typedef itk::DiscreteGaussianImageFilter<ImageType, ImageType> dgf;
typedef itk::MedianImageFilter<ImageType, ImageType> medf;
typename dgf::Pointer filter = dgf::New();
typename medf::Pointer filter2 = medf::New();
bool usespacing = false;
if( argc > 5 )
{
usespacing = atoi(argv[5]);
}
bool usemedian = false;
if( argc > 6 )
{
usemedian = atoi(argv[6]);
}
if( !usespacing )
{
filter->SetUseImageSpacingOff();
}
else
{
filter->SetUseImageSpacingOn();
}
if( !usemedian )
{
if( sigmaVector.size() == 1 )
{
filter->SetVariance( vnl_math_sqr( sigmaVector[0] ) );
}
else if( sigmaVector.size() == ImageDimension )
{
typename dgf::ArrayType varianceArray;
for( unsigned int d = 0; d < ImageDimension; d++ )
{
varianceArray[d] = vnl_math_sqr( sigmaVector[d] );
}
filter->SetVariance( varianceArray );
}
else
{
std::cerr << "Incorrect sigma vector size. Must either be of size 1 or ImageDimension." << std::endl;
}
filter->SetMaximumError( 0.01f );
filter->SetInput( image1 );
filter->Update();
varimage = filter->GetOutput();
}
else
{
typename ImageType::SizeType rad;
if( sigmaVector.size() == 1 )
{
rad.Fill( static_cast<unsigned long>( sigmaVector[0] ) );
}
else if( sigmaVector.size() == ImageDimension )
{
for( unsigned int d = 0; d < ImageDimension; d++ )
{
rad[d] = sigmaVector[d];
}
}
else
{
std::cerr << "Incorrect sigma vector size. Must either be of size 1 or ImageDimension." << std::endl;
}
filter2->SetRadius(rad);
filter2->SetInput( image1 );
filter2->Update();
varimage = filter2->GetOutput();
}
WriteImage<ImageType>( varimage, argv[4] );
return EXIT_SUCCESS;
}
// entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to
// 'main()'
int SmoothImage( std::vector<std::string> args, std::ostream* /*out_stream = NULL */ )
{
// put the arguments coming in as 'args' into standard (argc,argv) format;
// 'args' doesn't have the command name as first, argument, so add it manually;
// 'args' may have adjacent arguments concatenated into one argument,
// which the parser should handle
args.insert( args.begin(), "SmoothImage" );
int argc = args.size();
char* * argv = new char *[args.size() + 1];
for( unsigned int i = 0; i < args.size(); ++i )
{
// allocate space for the string plus a null character
argv[i] = new char[args[i].length() + 1];
std::strncpy( argv[i], args[i].c_str(), args[i].length() );
// place the null character in the end
argv[i][args[i].length()] = '\0';
}
argv[argc] = ITK_NULLPTR;
// class to automatically cleanup argv upon destruction
class Cleanup_argv
{
public:
Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ )
{
}
~Cleanup_argv()
{
for( unsigned int i = 0; i < argc_plus_one; ++i )
{
delete[] argv[i];
}
delete[] argv;
}
private:
char* * argv;
unsigned int argc_plus_one;
};
Cleanup_argv cleanup_argv( argv, argc + 1 );
// antscout->set_stream( out_stream );
if( argc < 4 )
{
std::cout << "Usage: " << std::endl;
std::cout << argv[0]
<<
" ImageDimension image.ext smoothingsigma outimage.ext {sigma-is-in-spacing-coordinates-0/1} {medianfilter-0/1}"
<< std::endl;
std::cout << " if median, then sigma means radius of filtering " << std::endl;
std::cout << " A separate sigma can be specified for each dimension, e.g., 1.5x1x2 " << std::endl;
if( argc >= 2 &&
( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) )
{
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
switch( atoi(argv[1]) )
{
case 2:
{
return SmoothImage<2>(argc, argv);
}
break;
case 3:
{
return SmoothImage<3>(argc, argv);
}
break;
case 4:
{
return SmoothImage<4>(argc, argv);
}
break;
default:
std::cout << "Unsupported dimension" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
} // namespace ants
enter code here
I think maybe you're making an assumption that bash does inline math on the command line. It doesn't. Try for example -
$ echo $216*512*512
$ 16*512*512
In fact, unless I'm missing some syntactic magic of which I'm unaware, the $2 is simply being interpreted as the second parameter to the parent script, as in -
$ function check() { echo $216*512*512; }
$ check fragile rock
$ rock16*512*512
See? However, I think what you've got here is a typo. The $2 was probably meant to be by itself, because the first parameter to your program is a selector that you use near the end to instantiate the templated version of your smoothing function. So, the second parameter to the calling script becomes the first parameter of SmoothImage, as in -
$DOANTS/SmoothImage $2 16*512*512 $RESULTS/Registration/NiftyReg/StructureGuided/Pat28_MLAF0113_pv_Aff_BSpline_Iso_ArgMax.nii.gz $1 $RESULTS/Registration/NiftyReg/StructureGuided/Pat28_${CASES[$i]}\_Prostate_Aff_BSpline_Iso_ArgMax_smoothed.nii.gz
If however, as you say, the size parameter is 16*512*512 or 216*512*512 as the case may be, you're still running into the problem that bash doesn't do math in that way on the command line. What you want is something like -
declare -ri size=16*512*512
# or just "let size=16*512*512", the "declare -ri" just makes it a read-only integer
$DOANTS/SmoothImage $2 $size $RESULTS/Registration/NiftyReg/StructureGuided/Pat28_MLAF0113_pv_Aff_BSpline_Iso_ArgMax.nii.gz $1 $RESULTS/Registration/NiftyReg/StructureGuided/Pat28_${CASES[$i]}\_Prostate_Aff_BSpline_Iso_ArgMax_smoothed.nii.gz
I think that's where you're headed. If you want some evidence that this works, on your bash command line, try -
$ let size=16*512*512
$ echo $size
$ 4194304
Your C++ is probably fine (though I haven't gone through it in detail, but at a glance it looks OK). The problem is your bash. If you want features like inline math, you might consider looking at Python to drive your C++ programs. Just a thought.
Related
I can't find any error that will lead to this result
I am new to C++ and want to test out how much I actually learned so I made this simple cRaZyTeXt generator. But there's a weird bug I can't find any way to solve. Codes are here: #include <iostream> #include <string> #include <algorithm> #include <windows.h> char convertToUppercase (char x) { int asciiCode {static_cast<int>(x) - 32}; char y {static_cast<char>(asciiCode)}; return y; } char convertToLowercase (char x) { int asciiCode {static_cast<int>(x) + 32}; char y {static_cast<char>(asciiCode)}; return y; } void toClipboard(const std::string &s){ OpenClipboard(0); EmptyClipboard(); HGLOBAL hg=GlobalAlloc(GMEM_MOVEABLE,s.size() + 1); if (!hg){ CloseClipboard(); return; } memcpy(GlobalLock(hg),s.c_str(),s.size() + 1); GlobalUnlock(hg); SetClipboardData(CF_TEXT,hg); CloseClipboard(); GlobalFree(hg); } int main() { std::cout << "Enter the text you want to convert into cRaZy TeXt: " << '\n'; std::string userInput {}; std::getline(std::cin >> std::ws, userInput); char userInputArray [userInput.size()]; std::copy(userInput.begin(), userInput.end(), userInputArray); char outputArray [userInput.size()]; for (int i = 0; i <= userInput.size(); ++i) { int x {static_cast<int>(userInputArray[i])}; if (i % 2 == 0) { if (x <= 90 && x >= 65) outputArray[i] = convertToLowercase(userInputArray[i]); else outputArray[i] = userInputArray[i]; } else { if (x <= 122 && x >= 97) outputArray[i] = convertToUppercase(userInputArray[i]); else outputArray[i] = userInputArray[i]; } } std::cout << outputArray << '\n'; toClipboard(outputArray); system("pause"); return 0; } when I enter Hello, world!, it can output hElLo, WoRlD! as exactly how I want it to be. proof But when I try my name is sean., its output would look like this: screenshot mY NaMe iS SeAn.#y name is sean.#%� What's more weird is that both my name is ma sean. and my name is sean ma. works fine. my name is ma sean. my name is sean ma. I have tried above four inputs in both release and debug configuration and it's all the same. Please elaborate on the issue and make the explanation friendlier for beginners. Any helps are appreciated. Thank you in advance.
For starters variable length arrays as for example the declaration of this array char userInputArray [userInput.size()]; is not a standard C++ feature. There is no need to use auxiliary arrays to perform the task. You could change the original object userInput of the type std::string itself. This variable length array char outputArray [userInput.size()]; does not contain a space for the terminating zero character '\0' to make the stored sequence of characters a string. As a result this output std::cout << outputArray << '\n'; invokes undefined behavior. This for loop for (int i = 0; i <= userInput.size(); ++i) leads to access memory beyond the declared variable length arrays because the valid range of indices is [ 0, userInput.size() ). Also it is a bad idea to use magic numbers like for example 65 or 90. This makes the code unreadable. If I have understood correctly what you need is a function like the following shown in the demonstrative program below. #include <iostream> #include <string> #include <cctype> std::string & cRaZyTeXt_generator( std::string &s ) { int upper_case = 1; for (auto &c : s) { if ( std::isalpha( static_cast< unsigned char >( c ) ) ) { if ( ( upper_case ^= 1 ) ) { c = std::toupper( static_cast< unsigned char >( c ) ); } else { c = std::tolower( static_cast< unsigned char >( c ) ); } } } return s; } int main() { std::string s( "Hello, World!" ); std::cout << s << '\n'; std::cout << cRaZyTeXt_generator( s ) << '\n'; } The program output is Hello, World! hElLo, WoRlD!
How can I use an * wildcard in an if statement?
I'm creating a simple program that is going to visit a website of the users choosing so I'm using an if statement like: If (url == "http://") { cout << ("Connecting to ") << url; } else { cout << ("Invalid URL"); } And I'm wondering how I can filter out strings that doesn't start with "http://" or "https://", I'm just starting out so help would be appreciated.
A clear, but not particularly fast way, is to use (assuming url is a std::string) if (url.substr(0, 7) != "http://" && url.substr(0, 8) != "https://"){ /*I don't start with http:// or https:// */ } Here I'm using substr to extract the start of a std::string then using the overloaded != operator. Note that if url is shorter than 7 or 8 characters, the behaviour is still well-defined. You could define static const char HTTP[] = "http://" and use sizeof(HTTP) - 1 &c. so you don't hardcode the lengths, but that might be going a step too far. For more generality you could venture into the murky world of regular expressions. See std::regex.
A possible option would be to store the known starting protocols into a vector of strings then use that vector and its fuctions as well as the strings functions to do your tests and if your url is a string object comparison is easy. #include <string> #include <vector> int main { const std::vector<std::string> urlLookUps { "http://", "https://" }; std::string url( "https://www.home.com" ); unsigned int size1 = urlLookUps[0].size(); unsigned int size2 = urlLookUps[1].size(); if ( url.compare( 0, size1, urlLookUps[0] ) == 0 || url.compare( 0, size2, urlLookUps[1] ) == 0 ) { std::cout << url << std::endl; } else { std::cout << "Invalid Address" << std::endl; } return 0; } EDIT You can take this to the next step and turn it into a simple function #include <string> #include <vector> void testUrls( const std::string& url, const std::vector<std::string>& urlLookUps ) { std::vector<unsigned int> sizes; for ( unsigned int idx = 0; idx < urlLookUps.size(); ++idx ) { sizes.push_back( urlLookUps[idx].size() ); } bool foundIt = false; for ( unsigned int idx = 0; idx < urlLookUps.size(); ++idx ) { if ( url.compare( 0, sizes[idx], urlLookUps[idx] ) == 0 ) { foundIt = true; break; } } if ( foundIt ) { std::cout << url << std::endl; } else { std::cout << "Invalid URL" << std::endl; } } // testUrls int main() { const std::vector<std::string> urlLookUps { "http://", "https://" }; std::string url1( "http://www.home.com" ); std::string url2( "https://www.home.com" ); std::string url3( "htt://www.home.com" ); testUrl( url1, urlLookUps ); testUrl( url2, urlLookUps ); testUrl( url3, urlLookUps ); return 0; } // main This way you can pass both the URL to the function as well as a container of url protocols that the user may want to populate themselves. This way the function will search through all the strings that are saved into the vector of strings.
Segmentation fault: 11 when iterating over arguments of argv
I write this C++ program, destinated to reproduce the echo command : #include <iostream> #include <queue> #include <string> #include <iterator> #include <unistd.h> using namespace std; int main(int argc, char *argv[]) { //Step 1: convert a silly char*[] to queue<string> queue<string> args; for(int i=1;i<=argc;i++) { args.push(string(argv[i])); } //Step 2: Use this queue<string> (because a non-used variable, that's useless) string arg, var, tos; bool showEndl = true; for(int i=0;i<=argc;i++) //The implementation of for arg in args is so crazy { arg = args.front(); //I can do arg = args[i] but that's not for nothing I make a queue. The cashier, she takes the customer front, she does not count the number of customers. args.pop(); //Pop the arg if(arg[0] == '$') //If that's a variable { var = ""; //Reset the variable 'var' to '' for(string::iterator it=arg.begin();it!=arg.end();it++) //Because C++ is so complicated. In Python, that's just var = arg[1:] { var += *it; } tos += string(getenv(var.c_str())); tos += ' '; } else if(arg == "-n") //Elif... No, C++ do not contains elif... Else if this is the -n argument. { showEndl = false; } else { tos += arg; tos += ' '; } } //Step 3 : Show the TO Show string. So easy. cout << tos; //Step 4 : Never forget the endl if(showEndl) { cout << endl; } string a; } It compiles fine, but when I run it, it tells me "Segmentation fault: 11" in the console. I use LLVM. What that means? Why that makes that? PS : I use LLVM.
The segmentation fault is due to memory access violation - dereferencing invalid pointer: for( int i = 1; i <= argc; i++) { args.push( string( argv[ i])); } When there are argc arguments sent to a program the last one is indexed with argc - 1. for( int i = 0; i < argc; i++) // includes also a name of a program, argv[ 0] { args.push( string( argv[ i])); } or: for( int i = 1; i < argc; i++) // excludes a name of a program, argv[ 0] { args.push( string( argv[ i])); } I suggest a use of debuger. It will show you the line causing a fault so you can investigate invalid pointer. Change also to: for( int i=0; i < args.size(); ++i) { arg = args.front();
C++ Map exc_bad_access (Apple only)
Code Reads from On Windows 7 and 8 it runs fine. However, when running in XCode 4 I get EXC_BAD_ACCESS on the second iteration when someone loads a map (select "Load Map" from title). You can download the source with the XCode project #include <string> #include <map> #include <iostream> std::map <std::string, std::string> info; std::string* get_key_val( std::string* line ) { std::string key_val[2]; int start, end; start = line->find_first_not_of( " " ); end = line->find_last_of( ":" ); if( start == -1 ) { return NULL; } else if( end == -1 ) { return NULL; } else { key_val[0] = line->substr( start, end - start ); } start = line->find_first_not_of(" ", end + 1); end = line->find_last_of( " \n\r" ); if( start == -1 ) { return NULL; } else if( end == -1 ) { return NULL; } else { key_val[1] = line->substr( start, end - start ); } return key_val; } void parse_from_line( std::string* line ) { std::string* keyv = get_key_val( line ); if( keyv[0].empty() == false && keyv[1].empty() == false ) info[ keyv[0] ] = keyv[1]; } int main( int argc, char* args[] ) { std::string line = "name: Foo"; parse_from_line( &line ); std::cout << "Hello " << info["name"].c_str(); }
Your get_key_val function starts like this: std::string* Map::get_key_val( std::string* line ) { std::string key_val[2]; It ends like this: return key_val; } You're returning a pointer to a stack variable. The key_val variable ceases to exist upon return from the function, so you have an invalid pointer, and the two string values in the array get destroyed. Subsequent behavior is undefined.
With move semantics in C++11 onwards, its less necessary to do this. You can just return std::string and the move operator should avoid any wasteful copies.
String and character mapping question for the guru's out there
Here's a problem thats got me stumped (solution wise): Given a str S, apply character mappings Cm = {a=(m,o,p),d=(q,u),...} and print out all possible combinations using C or C++. The string can be any length, and the number of character mappings varies, and there won't be any mappings that map to another map (thus avoiding circular dependencies). As an example: string abba with mappings a=(e,o), d=(g,h), b=(i) would print: abba,ebba,obba,abbe,abbo,ebbe,ebbo,obbe,obbo,aiba,aiia,abia,eiba,eiia,......
Definitely possible, not really difficult... but this will generate lots of strings that's for sure. The first thing to remark is that you know how many strings it's going to generate beforehand, so it's easy to do some sanity check :) The second: it sounds like a recursive solution would be easy (like many traversal problems). class CharacterMapper { public: CharacterMapper(): mGenerated(), mMapped() { for (int i = -128, max = 128; i != max; ++i) mMapped[i].push_back(i); // 'a' is mapped to 'a' by default } void addMapped(char origin, char target) { std::string& m = mMapped[origin]; if (m.find(target) == std::string::npos) m.push_back(target); } // addMapped void addMapped(char origin, const std::string& target) { for (size_t i = 0, max = target.size(); i != max; ++i) this->addMapped(origin, target[i]); } // addMapped void execute(const std::string& original) { mGenerated.clear(); this->next(original, 0); this->sanityCheck(original); this->print(original); } private: void next(std::string original, size_t index) { if (index == original.size()) { mGenerated.push_back(original); } else { const std::string& m = mMapped[original[index]]; for (size_t i = 0, max = m.size(); i != max; ++i) this->next( original.substr(0, index) + m[i] + original.substr(index+1), index+1 ); } } // next void sanityCheck(const std::string& original) { size_t total = 1; for (size_t i = 0, max = original.size(); i != max; ++i) total *= mMapped[original[i]].size(); if (total != mGenerated.size()) std::cout << "Failure: should have found " << total << " words, found " << mGenerated.size() << std::endl; } void print(const std::string& original) const { typedef std::map<char, std::string>::const_iterator map_iterator; typedef std::vector<std::string>::const_iterator vector_iterator; std::cout << "Original: " << original << "\n"; std::cout << "Mapped: {"; for (map_iterator it = mMapped.begin(), end = mMapped.end(); it != end; ++it) if (it->second.size() > 1) std::cout << "'" << it->first << "': '" << it->second.substr(1) << "'"; std::cout << "}\n"; std::cout << "Generated:\n"; for (vector_iterator it = mGenerated.begin(), end = mGenerated.end(); it != end; ++it) std::cout << " " << *it << "\n"; } std::vector<std::string> mGenerated; std::map<char, std::string> mMapped; }; // class CharacterMapper int main(int argc, char* argv[]) { CharacterMapper mapper; mapper.addMapped('a', "eo"); mapper.addMapped('d', "gh"); mapper.addMapped('b', "i"); mapper.execute("abba"); } And here is the output: Original: abba Mapped: {'a': 'eo''b': 'i''d': 'gh'} Generated: abba abbe abbo abia abie abio aiba aibe aibo aiia aiie aiio ebba ebbe ebbo ebia ebie ebio eiba eibe eibo eiia eiie eiio obba obbe obbo obia obie obio oiba oibe oibo oiia oiie oiio Yeah, rather lengthy, but there's a lot that does not directly participate to the computation (initialization, checks, printing). The core methods is next which implements the recursion.
EDIT: This should be the fastest and simplest possible algo. Some may argue with the style or portability; I think this is perfect for an embedded-type thing and I've spent long enough on it already. I'm leaving the original below. This uses an array for mapping. The sign bit is used to indicate the end of a mapping cycle, so the array type has to be larger than the mapped type if you want to use the full unsigned range. Generates 231M strings/sec or ~9.5 cycles/string on a 2.2GHz Core2. Testing conditions and usage as below. #include <iostream> using namespace std; int const alphabet_size = CHAR_MAX+1; typedef int map_t; // may be char or short, small performance penalty int const sign_bit = 1<< CHAR_BIT*sizeof(map_t)-1; typedef map_t cmap[ alphabet_size ]; void CreateMap( char *str, cmap &m ) { fill( m, m+sizeof(m)/sizeof(*m), 0 ); char *str_end = strchr( str, 0 ) + 1; str_end[-1] = ' '; // space-terminated strings char prev = ' '; for ( char *pen = str; pen != str_end; ++ pen ) { if ( * pen == ' ' ) { m[ prev ] |= sign_bit; prev = 0; } m[ * pen ] = * pen; if ( prev != ' ' ) swap( m[prev], m[ *pen ] ); prev = *pen; } for ( int mx = 0; mx != sizeof(m)/sizeof(*m); ++ mx ) { if ( m[mx] == 0 ) m[mx] = mx | sign_bit; } } bool NextMapping( char *s, char *s_end, cmap &m ) { for ( char *pen = s; pen != s_end; ++ pen ) { map_t oldc = *pen, newc = m[ oldc ]; * pen = newc & sign_bit-1; if ( newc >= 0 ) return true; } return false; } int main( int argc, char **argv ) { uint64_t cnt = 0; cmap m; CreateMap( argv[1], m ); char *s = argv[2], *s_end = strchr( s, 0 ); do { ++ cnt; } while ( NextMapping( s, s_end, m ) ); cerr << cnt; return 0; } ORIGINAL: Not as short or robust as I'd like, but here's something. Requires that the input string always contain the alphabetically first letter in each replacement set Execute a la maptool 'aeo dgh bi' abbd Output is in reverse-lexicographical order Performance of about 22 cycles/string (100M strings/sec at 2.2 GHz Core2) BUT my platform is trying to be clever with strings, slowing it down If I change it to use char* strings instead, it runs at 142M strings/sec (~15.5 cycles/string) Should be possible to go faster using a char[256] mapping table and another char[256] specifying which chars end a cycle. The map data structure is an array of nodes linked into circular lists. #include <iostream> #include <algorithm> using namespace std; enum { alphabet_size = UCHAR_MAX+1 }; struct MapNode { MapNode *next; char c; bool last; MapNode() : next( this ), c(0), last(false) {} }; void CreateMap( string s, MapNode (&m)[ alphabet_size ] ) { MapNode *mprev = 0; replace( s.begin(), s.end(), ' ', '\0' ); char *str = const_cast<char*>(s.c_str()), *str_end = str + s.size() + 1; for ( char *pen = str; pen != str_end; ++ pen ) { if ( mprev == 0 ) sort( pen, pen + strlen( pen ) ); if ( * pen == 0 ) { if ( mprev ) mprev->last = true; mprev = 0; continue; } MapNode &mnode = m[ * pen ]; if ( mprev ) swap( mprev->next, mnode.next ); // link node in mnode.c = * pen; // tell it what char it is mprev = &mnode; } // make it easier to tell that a node isn't in any map for ( MapNode *mptr = m; mptr != m + alphabet_size; ++ mptr ) { if ( mptr->next == mptr ) mptr->next = 0; } } bool NextMapping( string &s, MapNode (&m)[ alphabet_size ] ) { for ( string::iterator it = s.begin(); it != s.end(); ++ it ) { MapNode &mnode = m[ * it ]; if ( mnode.next ) { * it = mnode.next->c; if ( ! mnode.last ) return true; } } return false; } int main( int argc, char **argv ) { MapNode m[ alphabet_size ]; CreateMap( argv[1], m ); string s = argv[2]; do { cerr << s << endl; } while ( NextMapping( s, m ) ); return 0; }
The way I would go about this is to create an array of indexes the same length as the string, all initialized at zero. We then treat this array of indexes as a counter to enumerate all the possible mappings of our source string. A 0 index maps that position in the string to the first mapping for that character, a 1 to the second, etc. We can step through them in order by just incrementing the last index in the array, carrying over to the next position when we reach the maximum number of mappings for that position. To use your example, we have the mappings 'a' => 'e', 'o' 'b' => 'i' With the input string "abba", we need a four element array for our indexes: [0,0,0,0] => "abba" [0,0,0,1] => "abbe" [0,0,0,2] => "abbo" [0,0,1,0] => "abia" [0,0,1,1] => "abie" [0,0,1,2] => "abio" [0,1,0,0] => "aiba" [0,1,0,1] => "aibe" [0,1,0,2] => "aibo" [0,1,1,0] => "aiia" [0,1,1,1] => "aiie" [0,1,1,2] => "aiio" [1,0,0,0] => "ebba" [1,0,0,1] => "ebbe" [1,0,0,2] => "ebbo" [1,0,1,0] => "ebia" [1,0,1,1] => "ebie" [1,0,1,2] => "ebio" [1,1,0,0] => "eiba" [1,1,0,1] => "eibe" [1,1,0,2] => "eibo" [1,1,1,0] => "eiia" [1,1,1,1] => "eiie" [1,1,1,2] => "eiio" [2,0,0,0] => "obba" [2,0,0,1] => "obbe" [2,0,0,2] => "obbo" [2,0,1,0] => "obia" [2,0,1,1] => "obie" [2,0,1,2] => "obio" [2,1,0,0] => "oiba" [2,1,0,1] => "oibe" [2,1,0,2] => "oibo" [2,1,1,0] => "oiia" [2,1,1,1] => "oiie" [2,1,1,2] => "oiio" Before we start generating these strings, we're going to need somewhere to store them, which in C, means that we're going to have to allocate memory. Fortunately, we know the length of these strings already, and we can figure out the number of strings we're going to generate - it's just the product of the number of mappings for each position. While you can return them in an array, I prefer to use a callback to return them as I find them. #include <string.h> #include <stdlib.h> int each_combination( char const * source, char const * mappings[256], int (*callback)(char const *, void *), void * thunk ) { if (mappings == NULL || source == NULL || callback == NULL ) { return -1; } else { size_t i; int rv; size_t num_mappings[256] = {0}; size_t const source_len = strlen(source); size_t * const counter = calloc( source_len, sizeof(size_t) ); char * const scratch = strdup( source ); if ( scratch == NULL || counter == NULL ) { rv = -1; goto done; } /* cache the number of mappings for each char */ for (i = 0; i < 256; i++) num_mappings[i] = 1 + (mappings[i] ? strlen(mappings[i]) : 0); /* pass each combination to the callback */ do { rv = callback(scratch, thunk); if (rv != 0) goto done; /* increment the counter */ for (i = 0; i < source_len; i++) { counter[i]++; if (counter[i] == num_mappings[(unsigned char) source[i]]) { /* carry to the next position */ counter[i] = 0; scratch[i] = source[i]; continue; } /* use the next mapping for this character */ scratch[i] = mappings[(unsigned char) source[i]][counter[i]-1]; break; } } while(i < source_len); done: if (scratch) free(scratch); if (counter) free(counter); return rv; } } #include <stdio.h> int print_each( char const * s, void * name) { printf("%s:%s\n", (char const *) name, s); return 0; } int main(int argc, char ** argv) { char const * mappings[256] = { NULL }; mappings[(unsigned char) 'a'] = "eo"; mappings[(unsigned char) 'b'] = "i"; each_combination( "abba", mappings, print_each, (void *) "abba"); each_combination( "baobab", mappings, print_each, (void *) "baobab"); return 0; }
You essentially want to do a depth-first search (DFS) or any other traversal down a directed acyclic word graph (DAWG). I will post some code shortly.
There is a link to the snippets archive which does that, here, Permute2.c. There is another variant of the string permutation (I guess you could then filter out those that are not in the map!) See here on the 'snippets' archive... Hope this helps, Best regards, Tom.
simple, recursive permute, with using char map[256] char *map [256]; /* permute the ith char in s */ perm (char *s, int i) { if (!s) return; /* terminating condition */ if (s[i] == '\0') { /* add "s" to a string array if we want to store the permutations */ printf("%s\n", s); return; } char c = s[i]; char *m = map [c]; // printf ("permuting at [%c]: %s\n", c, m); int j=0; /* do for the first char, then use map chars */ do { perm (s, i+1); s[i] = m[j]; } while (m[j++] != '\0'); /* restore original char here, used for mapping */ s[i] = c; return; } int main () { /* map table initialization */ map['a'] = "eo\0"; map['b'] = "i\0"; map['d'] = "gh\0"; /* need modifyable sp, as we change chars in position, sp="abba" will not work! */ char *sp = malloc (10); strncpy (sp, "abba\0", 5); perm (sp, 0); return 0; }