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