1 // This is mul/mbl/mbl_mask.cxx
2 
3 //:
4 // \file
5 // \author Barry Skellern
6 // \brief Class representing a binary mask, and related functions
7 
8 #include <set>
9 #include <map>
10 #include <iostream>
11 #include <fstream>
12 #include <string>
13 #include "mbl_mask.h"
14 #ifdef _MSC_VER
15 #  include "vcl_msvc_warnings.h"
16 #endif
17 #include "vul/vul_string.h"
18 #include <mbl/mbl_exception.h>
19 
20 
21 
22     //: Given a collection of indices, produce a collection of masks that isolate each indexed set
23     //    The input index set does not need to be zero based or continuous
24     //    The output vector of masks is sorted such that
25     //    for example: (1,4,2,1,2) will make three masks: (1,0,0,1,0), (0,0,1,0,1) and (0,1,0,0,0)
26     //    which correspond to the sorted index sets 1,2,4
mbl_masks_from_index_set(const std::vector<unsigned> & indices,std::vector<mbl_mask> & masks)27 void mbl_masks_from_index_set(const std::vector<unsigned> & indices,
28                               std::vector<mbl_mask> & masks)
29 {
30   masks.clear();
31   unsigned n = indices.size(), n_masks = 0;
32   std::set<unsigned> used_indices;
33   std::map<unsigned, unsigned> ordering;
34 
35   for (unsigned i = 0 ; i < n ; ++i)
36     used_indices.insert(indices[i]);
37 
38   for (const auto & used_indice : used_indices)
39   {
40     ordering[used_indice] = n_masks++;
41     masks.emplace_back(n);
42   }
43 
44   for (unsigned i = 0 ; i < n ; ++i)
45     masks[ordering[indices[i]]][i] = true;
46 }
47 
48 
49     //: Replace 'true' values in B with values taken from A. size of A must match 'true' count in B
mbl_mask_on_mask(const mbl_mask & A,mbl_mask & B)50 void mbl_mask_on_mask(const mbl_mask & A, mbl_mask & B)
51 {
52   unsigned nA = A.size();
53   unsigned nB = 0;
54   for (unsigned i = 0 ; i < B.size() ; ++i) nB += B[i];
55   if (nA != nB)
56     throw std::out_of_range("mbl_mask: Length of A mismatch with number of true elements of B");
57 
58   for (unsigned i = 0, j = 0 ; i < B.size() ; ++i)
59     if (B[i]) B[i] = A[j++];
60 }
61 
62 
63     //: Apply an "AND" (rule 0001) logical operation between two masks
mbl_mask_logic_and(const mbl_mask & A,mbl_mask & B)64 void mbl_mask_logic_and(const mbl_mask & A, mbl_mask & B)
65 {
66   mbl_mask_logic(A, B, "0001");
67 }
68 
69 //: Apply an "OR" (rule 0111) logical operation between two masks
mbl_mask_logic_or(const mbl_mask & A,mbl_mask & B)70 void mbl_mask_logic_or(const mbl_mask & A, mbl_mask & B)
71 {
72   mbl_mask_logic(A, B, "0111");
73 }
74 
75     //: Apply an "XOR" (rule 0110) logical operation between two masks
mbl_mask_logic_xor(const mbl_mask & A,mbl_mask & B)76 void mbl_mask_logic_xor(const mbl_mask & A, mbl_mask & B)
77 {
78   mbl_mask_logic(A, B, "0110");
79 }
80 
81     //: Apply a "NOR" (rule 1000) logical operation between two masks
mbl_mask_logic_nor(const mbl_mask & A,mbl_mask & B)82 void mbl_mask_logic_nor(const mbl_mask & A, mbl_mask & B)
83 {
84   mbl_mask_logic(A, B, "1000");
85 }
86 
87     //: Apply an "XNOR" (rule 1001) logical operation between two masks
mbl_mask_logic_xnor(const mbl_mask & A,mbl_mask & B)88 void mbl_mask_logic_xnor(const mbl_mask & A, mbl_mask & B)
89 {
90   mbl_mask_logic(A, B, "1001");
91 }
92 
93     //: Apply an "NAND" (rule 1110) logical operation between two masks
mbl_mask_logic_nand(const mbl_mask & A,mbl_mask & B)94 void mbl_mask_logic_nand(const mbl_mask & A, mbl_mask & B)
95 {
96   mbl_mask_logic(A, B, "1110");
97 }
98 
99 
100     //: Apply a general logical operation between two masks
mbl_mask_logic(const mbl_mask & A,mbl_mask & B,const std::string & operation)101 void mbl_mask_logic(const mbl_mask & A, mbl_mask & B, const std::string & operation)
102 {
103   if (A.size() != B.size())
104     throw std::out_of_range("mbl_mask_logic: Mask lengths differ");
105 
106   // Validate the operation to perform and parse into vector
107 
108   if (operation.length() != 4)
109     throw std::length_error("mbl_mask_logic: Operation must be of length 4");
110   std::vector<bool> op_rule(4);
111   for (unsigned i = 0 ; i < 4 ; ++i)
112   {
113     if (operation[i] == '0') op_rule[i] = false;
114     else if (operation[i] == '1') op_rule[i] = true;
115     else throw std::invalid_argument("mbl_mask_logic: Invalid character in operation string - must contain only '0' or '1'");
116   }
117 
118   // Apply the operation in place
119   for (unsigned i = 0 ; i < A.size() ; ++i)
120     B[i] = op_rule[2*A[i] + B[i]]; // consider AB as 2bit binary, converted to decimal index into rule
121 }
122 
123 
124     //: Save to file
mbl_save_mask(const mbl_mask & mask,std::ostream & stream)125 void mbl_save_mask(const mbl_mask & mask, std::ostream & stream)
126 {
127   auto it = mask.begin();
128   const std::vector<bool>::const_iterator & end = mask.end();
129   for (; it != end; ++it)
130     stream << *it << std::endl;
131 }
132 
133     //: Save to file
mbl_save_mask(const mbl_mask & mask,const char * filename)134 void mbl_save_mask(const mbl_mask & mask, const char * filename)
135 {
136   std::ofstream stream(filename);
137   if (!stream)
138     mbl_exception_throw_os_error(filename);
139   mbl_save_mask(mask, stream);
140 }
141 
142     //: Save to file
mbl_save_mask(const mbl_mask & mask,const std::string & filename)143 void mbl_save_mask(const mbl_mask & mask, const std::string &filename)
144 {
145   std::ofstream stream(filename.c_str());
146   if (!stream)
147     mbl_exception_throw_os_error(filename);
148   mbl_save_mask(mask, stream);
149 }
150 
151     //: Load from file
mbl_load_mask(mbl_mask & mask,std::istream & stream)152 void mbl_load_mask(mbl_mask & mask, std::istream & stream)
153 {
154   mask.clear();
155   std::string line;
156   while (stream.good())
157   {
158     char c='X';
159     stream >> std::ws >> c;
160     if (stream.eof()) break;
161     if (c == '0') mask.push_back(false);
162     else if (c == '1') mask.push_back(true);
163     else
164     {
165       mask.clear();
166       throw mbl_exception_parse_file_error(std::string("Unable to parse mask value " +
167         vul_string_escape_ctrl_chars(std::string(1,c)) ), "" );
168     }
169   }
170 }
171 
172     //: Load from file
mbl_load_mask(mbl_mask & mask,const char * filename)173 void mbl_load_mask(mbl_mask & mask, const char * filename)
174 {
175   std::ifstream stream(filename);
176   if (!stream)
177     mbl_exception_throw_os_error(filename);
178   try
179   {
180     mbl_load_mask(mask, stream);
181   }
182   catch (mbl_exception_parse_file_error & e)
183   {
184     throw mbl_exception_parse_file_error(e.what(), filename);
185   }
186 }
187 
188     //: Load from file
mbl_load_mask(mbl_mask & mask,const std::string & filename)189 void mbl_load_mask(mbl_mask & mask, const std::string &filename)
190 {
191   mbl_load_mask( mask, filename.c_str() );
192 }
193 
194 
195 //: Convert a mask to a list of indices.
mbl_mask_to_indices(const mbl_mask & mask,std::vector<unsigned> & inds)196 void mbl_mask_to_indices(const mbl_mask& mask, std::vector<unsigned>& inds)
197 {
198   inds.clear();
199   for (unsigned i=0,n=mask.size(); i<n; ++i)
200   {
201     if (mask[i]) inds.push_back(i);
202   }
203 }
204 
205 
206 //: Convert a list of indices to a mask.
mbl_indices_to_mask(const std::vector<unsigned> & inds,const unsigned n,mbl_mask & mask)207 void mbl_indices_to_mask(const std::vector<unsigned>& inds,
208                          const unsigned n,
209                          mbl_mask& mask)
210 {
211   mask.resize(n, false);
212   for (unsigned int ind : inds)
213   {
214     mask[ind]=true;
215   }
216 }
217