NCBI C++ ToolKit
cuCdFamily.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: cuCdFamily.cpp 91535 2020-11-09 19:53:43Z lanczyck $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Charlie Liu
27  *
28  * File Description: Implement cdt_cd_family.hpp.
29  * part of CDTree app
30  */
31 #include <ncbi_pch.hpp>
34 #include <algorithm>
35 
36 //using namespace std;
38 BEGIN_SCOPE(cd_utils)
39 
40 
41 CDNode::CDNode(CCdCore* theCd) : cd(theCd), comParents(), selected(true) {
42 }
43 
45  CDFamilyBase()
46 {
47  setRootCD(rootCD);
48 }
49 
50 
52  CDFamilyBase(), m_rootCD(0)
53 {
54 }
55 
57 {
58  if (!rootCD)
59  return end();
60  if (begin() != end()) //can't add root if there is already one
61  return end();
62  m_rootCD = rootCD;
63  return insert(begin(), m_rootCD);
64 }
65 
67 {
68  return m_rootCD;
69 }
70 
71 
72 //operations to build the family tree
74 {
75  CDFamilyIterator pit;
76  if (parentCD == 0)
77  pit = end();
78  else
79  pit = findCD(parentCD);
80  if (pit == end())
81  return pit;
82  else
83  return append_child(pit, CDNode(cd));
84 }
85 
87 {
88  CDFamilyIterator cDIterator = findCD(cd);
89  if (cDIterator == end())
90  return false;
91  else
92  {
93  erase(cDIterator);
94  return true;
95  }
96 }
97 
98 //operations to navigate the family tree
100 {
101  // return find(begin(), end(), CDNode(cd));
102  CDNode node(cd);
103  for (iterator it = begin(); it != end(); ++it) {
104  if (*it == cd) {
105  return it;
106  }
107  }
108  return end();
109 }
110 
112 {
113  // return find(begin(), end(), CDNode(cd));
114  string acc = (cd) ? cd->GetAccession() : "";
115  for (iterator it = begin(); it != end(); ++it) {
116  if (it->cd->GetAccession() == acc) {
117  return it;
118  }
119  }
120  return end();
121 }
122 
124 {
125  for (iterator it = begin(); it != end(); ++it) {
126  if (it->cd->GetAccession() == acc) {
127  return it;
128  }
129  }
130  return end();
131 }
132 
133 //parent=0, get from root
134 void CDFamily::getChildren(vector<CCdCore*>& cds, CCdCore* parentCD) const
135 {
136  CDFamilyIterator pit;
137  if (parentCD == 0)
138  pit = begin();
139  else
140  pit = findCD(parentCD);
141  getChildren(cds, pit);
142 }
143 
144 void CDFamily::getChildren(vector<CCdCore*>& cds, CDFamilyIterator pit) const
145 {
146  if (pit == end())
147  return;
148  CDFamily::sibling_iterator sit = pit.begin();
149  while (sit != pit.end())
150  {
151  cds.push_back(sit->cd);
152  ++sit;
153  }
154 }
155 
156 void CDFamily::getChildren(vector<CDFamilyIterator>& cdITs, CDFamilyIterator pit) const
157 {
158  if (pit == end())
159  return;
160  CDFamily::sibling_iterator sit = pit.begin();
161  while (sit != pit.end())
162  {
163  //can be a problem because push_back() takes a reference
164  cdITs.push_back(CDFamilyIterator(sit));
165  ++sit;
166  }
167 }
168 
169 void CDFamily::getDescendants(vector<CCdCore*>& cds, CCdCore* parentCd) const {
170  vector<CCdCore*> tmpCds;
171  getChildren(tmpCds, parentCd);
172  for (unsigned int i = 0; i < tmpCds.size(); ++i) {
173  cds.push_back(tmpCds[i]);
174  getDescendants(cds, tmpCds[i]);
175  }
176 }
177 
178 void CDFamily::getDescendants(set<CCdCore*>& cds, CCdCore* parentCd) const {
179  vector<CCdCore*> tmpCds;
180  cds.clear();
181  getDescendants(tmpCds, parentCd);
182  for (unsigned int i = 0; i < tmpCds.size(); ++i) {
183  cds.insert(tmpCds[i]);
184  }
185 }
186 
187 void CDFamily::subfamily(CDFamilyIterator cit, CDFamily*& subfam, bool childrenOnly)
188 {
189  vector<CCdCore*> cds;
190  cds.push_back(cit->cd);
191  if (childrenOnly)
192  getChildren(cds, cit);
193  else
194  getDescendants(cds,cit->cd);
195  vector<CDFamily*> families;
196  createFamilies(cds, families);
197  subfam = families[0];
198 }
199 
201 {
202  CDFamilyIterator it = findCD(childCD);
203  if (it == end())
204  return 0;
205  CDFamilyIterator pit = parent(it);
206  if (pit.node)
207  return pit->cd;
208  else
209  return 0;
210 }
211 
212 void CDFamily::selectCDs(const vector< CCdCore* > & cds)
213 {
214  for (CDFamilyIterator fit = begin(); fit != end(); ++fit)
215  {
216  if (find(cds.begin(), cds.end(), fit->cd) != cds.end())
217  fit->selected = true;
218  else
219  fit->selected = false;
220  }
221 }
222 
224 {
225  for (CDFamilyIterator fit = begin(); fit != end(); ++fit)
226  {
227  fit->selected = true;
228  }
229 }
230 
231 int CDFamily::getSelectedCDs(vector<CCdCore*>& cds)
232 {
233  for (CDFamilyIterator fit = begin(); fit != end(); ++fit)
234  {
235  if (fit->selected)
236  cds.push_back(fit->cd);
237  }
238  return cds.size();
239 }
240 
241 
243 {
244  return size();
245 }
246 
247 int CDFamily::getAllCD(vector<CCdCore*>& cds) const
248 {
249  CDFamilyIterator cit = begin();
250  for (; cit != end(); ++cit)
251  cds.push_back(cit->cd);
252  return cds.size();
253 }
254 
255 // Return list CDs on the direct (classical) path from initialCD to the root.
256 // 'initialCD' is the first in the path.
257 int CDFamily::getPathToRoot(CCdCore* initialCD, vector<CCdCore*>& path) const {
258 
259  CCdCore* currentCd;
260  CCdCore* parentCd = initialCD;
261 
262  path.clear();
263 
264  // Make sure initialCD is in the family.
265  if (findCD(initialCD) != end()) {
266  while (parentCd) {
267  currentCd = parentCd;
268  path.push_back(currentCd);
269  parentCd = getClassicalParent(currentCd);
270  }
271  }
272 
273  return path.size();
274 }
275 
276 CDFamilyIterator CDFamily::convergeTo(CCdCore* cd1, CCdCore* cd2, vector<CCdCore*>& path1, vector<CCdCore*>& path2) const
277 {
278  vector<CCdCore*> pathToRoot1, pathToRoot2;
279  getPathToRoot(cd1, pathToRoot1);
280  getPathToRoot(cd2, pathToRoot2);
281  vector<CCdCore*>::reverse_iterator rit1 = pathToRoot1.rbegin();
282  vector<CCdCore*>::reverse_iterator rit2 = pathToRoot2.rbegin();
283  vector<CCdCore*>::reverse_iterator lastConvergedIt1, lastConvergedIt2;
284  if( (rit1 == pathToRoot1.rend()) || (rit2 == pathToRoot2.rend()))
285  return end();
286  assert((*rit1) = (*rit2)); //both should point to the root
287  CCdCore* lastConvergedPoint = *rit1;
288  for (; rit1 != pathToRoot1.rend() && rit2 != pathToRoot2.rend(); rit1++, rit2++)
289  {
290  if (*rit1 == *rit2) {
291  lastConvergedPoint = *rit1;
292  lastConvergedIt1 = rit1;
293  lastConvergedIt2 = rit2;
294  } else {
295  break;
296  }
297  }
298 
299  // Construct the paths from cd1/cd2 to the lastConvergedPoint
300  path1.clear();
301  for (rit1 = lastConvergedIt1; rit1 != pathToRoot1.rend(); ++rit1) {
302  path1.push_back(*rit1);
303  }
304 // path1.insert(path1.end(), lastConvergedIt1, pathToRoot1.rend()); // breaks Workshop builds
305  reverse(path1.begin(), path1.end());
306 
307  path2.clear();
308  for (rit2 = lastConvergedIt2; rit2 != pathToRoot2.rend(); ++rit2) {
309  path2.push_back(*rit2);
310  }
311 // path2.insert(path2.end(), lastConvergedIt2, pathToRoot2.rend()); // breaks Workshop builds
312  reverse(path2.begin(), path2.end());
313 
314  return findCD(lastConvergedPoint);
315 }
316 
317 
318 CDFamilyIterator CDFamily::convergeTo(CCdCore* cd1, CCdCore* cd2, bool byAccession)const
319 {
320  vector<CCdCore*> path1, path2;
321  getPathToRoot(cd1, path1);
322  getPathToRoot(cd2, path2);
323  vector<CCdCore*>::reverse_iterator rit1 = path1.rbegin();
324  vector<CCdCore*>::reverse_iterator rit2 = path2.rbegin();
325  if( (rit1 == path1.rend()) || (rit2 == path2.rend()))
326  return end();
327  assert((*rit1) = (*rit2)); //both should point to the root
328  CCdCore* lastConvergedPoint = *rit1;
329  for (; rit1 != path1.rend() && rit2 != path2.rend(); rit1++, rit2++)
330  {
331  if ((!byAccession && *rit1 == *rit2) || (byAccession && (*rit1)->GetAccession() == (*rit2)->GetAccession()))
332  lastConvergedPoint = *rit1;
333  else
334  break;
335  }
336  return (byAccession) ? findCDByAccession(lastConvergedPoint) : findCD(lastConvergedPoint);
337 }
338 
339 CDFamilyIterator CDFamily::convergeTo(const set<CCdCore*>& cds, bool byAccession)const
340 {
341  if (cds.size() == 0)
342  return end();
344  CCdCore* lastConvergedPoint = *cit;
345  CDFamilyIterator fit = (byAccession) ? findCDByAccession(lastConvergedPoint) : findCD(lastConvergedPoint);
346  if (fit == begin() || fit == end())
347  return fit;
348  cit++;
349  for (; cit != cds.end(); cit++)
350  {
351  fit = convergeTo(lastConvergedPoint, *cit, byAccession);
352  if (fit == begin() || fit == end())
353  break;
354  lastConvergedPoint = fit->cd;
355  }
356  return fit;
357 }
358 
359 
360 // Return list of all CDs in the family not on the direct (classical) path from initialCD to the root.
361 // Returns all CDs if initialCD is null.
362 int CDFamily::getCdsNotOnPathToRoot(CCdCore* initialCD, vector<CCdCore*>& notOnPath) const {
363 
364  notOnPath.clear();
365 
366  // Make sure initialCD is in the family.
367  if (findCD(initialCD) != end()) {
368  for (CDFamilyIterator cit = begin(); cit != end(); ++cit) {
369  if (initialCD) {
370  if (cit->cd != initialCD && !isDirectAncestor(initialCD, cit->cd)) {
371  notOnPath.push_back(cit->cd);
372  }
373  } else {
374  notOnPath.push_back(cit->cd);
375  }
376  }
377  }
378  return notOnPath.size();
379 }
380 
381 
382 bool CDFamily::isDirectAncestor(CCdCore* cd, CCdCore* potentialAncestorCd) const {
383 
384  bool isAncestor = false;
385  CCdCore* parent;
386  if (cd && potentialAncestorCd && cd != potentialAncestorCd) {
387  if (potentialAncestorCd == getRootCD()) { // root is everyone's ancestor
388  isAncestor = true;
389  } else {
391  while (parent && !isAncestor) {
392  if (parent == potentialAncestorCd) {
393  isAncestor = true;
394  } else {
396  }
397  }
398  }
399  }
400  return isAncestor;
401 }
402 
403 bool CDFamily::isDescendant(CCdCore* cd, CCdCore* potentialDescendantCd) const {
404 
405  return isDirectAncestor(potentialDescendantCd, cd);
406 }
407 
408 bool CDFamily::IsFamilyValid(const CDFamily* family, string& err) {
409  bool hasError = false;
410  if (!family) {
411  err.append("Null CDFamily Object.\n");
412  hasError = true;
413  } else {
414  if (family->getRootCD() == NULL) {
415  err.append("CDFamily Object Has No Root.\n");
416  hasError = true;
417  } else if (family->getCDCounts() <= 0) {
418  err.append("CDFamily Object With No CDs.\n");
419  hasError = true;
420  }
421 
422  }
423  return (!hasError);
424 }
425 
426 void CDFamily::inspect()const
427 {
428  CDFamilyIterator fit = begin();
429  for(; fit != end(); ++fit)
430  {
431  string acc;
432  acc = fit->cd->GetAccession();
433  }
434 }
435 
437 {
438 }
439 
440 bool CDFamily::isDup(CDFamily& one, vector<CDFamily>& all)
441 {
442  int occurrence = 0;
443  CCdCore* cd = one.getRootCD();
444  for (unsigned int i = 0; i < all.size(); i++)
445  {
446  if (all[i].findCD(cd) != all[i].end())
447  occurrence++;
448  }
449  return occurrence > 1; // 1 = find self only, not a dup
450 }
451 
452 CDFamily* CDFamily::findFamily(CCdCore* cd, vector<CDFamily>& families)
453 {
454  for (unsigned int i = 0; i < families.size(); i++)
455  {
456  if (families[i].findCD(cd) != families[i].end())
457  return &(families[i]);
458  }
459  return 0;
460 }
461 
462 // In the vector, pass back pointers to vs copies of the families.
463 // Requires that the caller cleans up the families after!!
464 int CDFamily::createFamilies(vector<CCdCore*>& cds, vector<CDFamily*>& families)
465 {
466  vector<CCdCore*>::iterator cdIterator = cds.begin();
467  while(cdIterator != cds.end())
468  {
469  CCdCore* cd = *cdIterator;
470  if (!findParent(cd, cds)) //no parent, then use this cd as a root of family
471  {
472  CDFamily* cdFamily = new CDFamily(cd);
473  cds.erase(cdIterator);
474  extractFamily(cd, *cdFamily, cds);
475  families.push_back(cdFamily);
476  cdIterator = cds.begin();
477  }
478  else
479  ++cdIterator;
480  }
481  return families.size(); //all cds should be added
482 }
483 
484 void CDFamily::extractFamily(CCdCore* parentCD, CDFamily& cdFamily, vector<CCdCore*>& cds)
485 {
486  set<int> children;
487  //parentCD= cdFamily.getRootCD();
488  if (findChildren(parentCD, cds, children))
489  {
490  //add children
491  for (set<int>::iterator sit = children.begin(); sit != children.end(); ++sit)
492  {
493  cdFamily.addChild(cds[*sit], parentCD);
494  }
495  //remove added children from cds
496  vector<CCdCore*> tmp(cds);
497  cds.clear();
498  for (unsigned int i = 0; i < tmp.size(); i++)
499  {
500  if (children.find(i) == children.end())
501  {
502  cds.push_back(tmp[i]);
503  }
504  }
505  // extract the subfamily for each children
506  for (set<int>::iterator sit = children.begin(); sit != children.end(); ++sit)
507  {
508  extractFamily(tmp[*sit], cdFamily, cds);
509  }
510  }
511 }
512 
513 bool CDFamily::findParent(CCdCore* cd, vector<CCdCore*>& cds)
514 {
515  string acc = cd->GetClassicalParentAccession();
516  for (unsigned int i = 0; i < cds.size(); i++)
517  {
518  if (cds[i] != cd)
519  if (acc.compare(cds[i]->GetAccession()) == 0)
520  return true;
521  }
522  return false;
523 }
524 
525 bool CDFamily::findChildren(CCdCore* cd, vector<CCdCore*>& cds, set<int>& children)
526 {
527  string acc = cd->GetAccession();
528  for (int i = 0; i < (int) cds.size(); i++)
529  {
530  if (cds[i] != cd)
531  if (acc.compare(cds[i]->GetClassicalParentAccession()) == 0)
532  children.insert(i);
533  }
534  return children.size() > 0;
535 }
536 
538 {
539  CNcbiOstrstream oss;
541  return CNcbiOstrstreamToString(oss);
542 }
543 
544 void CDFamily::getNewickRepresentation(std::ostream& os, const CDFamilyIterator& cursor) const
545 {
546  static const string underscore("_");
547  static unsigned int n;
548 
549  if (!os.good())
550  return;
551 
552  string label;
553  bool isRoot = (cursor == begin());
554 
555  if (isRoot) n = 1;
556  label = NStr::UIntToString(n) + underscore + cursor->cd->GetAccession();
557  ++n;
558 
559  // if leaf, print leaf and return
560  if (cursor.number_of_children() == 0) {
561  os << label;
562  if (number_of_siblings(cursor) > 1)
563  os << ',';
564  return;
565  } else {
566  os << '(';
567 
568  // print each child
569  sibling_iterator sib = cursor.begin();
570  while (sib != cursor.end()) {
571  getNewickRepresentation(os, sib); //recursive
572  ++sib;
573  }
574 
575  // print ) <name>
576  os << ')' << label;
577  if (isRoot) {
578  os << ';';
579  } else {
580  if (number_of_siblings(cursor) > 1)
581  os << ',';
582  }
583  }
584  return;
585 }
586 
587 END_SCOPE(cd_utils)
string GetAccession(int &Version) const
Definition: cuCdCore.cpp:81
string GetClassicalParentAccession(int &Version) const
Definition: cuCdCore.cpp:1904
CCdCore * getClassicalParent(CCdCore *childCD) const
Definition: cuCdFamily.cpp:200
static bool findParent(CCdCore *cd, vector< CCdCore * > &cds)
Definition: cuCdFamily.cpp:513
bool isDescendant(CCdCore *cd, CCdCore *potentialDescendantCd) const
Definition: cuCdFamily.cpp:403
int getCdsNotOnPathToRoot(CCdCore *initialCD, vector< CCdCore * > &notOnPath) const
Definition: cuCdFamily.cpp:362
virtual ~CDFamily()
Definition: cuCdFamily.cpp:436
CDFamilyIterator convergeTo(CCdCore *cd1, CCdCore *cd2, bool byAccession=false) const
Definition: cuCdFamily.cpp:318
void inspect() const
Definition: cuCdFamily.cpp:426
void getDescendants(vector< CCdCore * > &cds, CCdCore *parentCD) const
Definition: cuCdFamily.cpp:169
int getPathToRoot(CCdCore *initialCD, vector< CCdCore * > &path) const
Definition: cuCdFamily.cpp:257
int getCDCounts() const
Definition: cuCdFamily.cpp:242
int getAllCD(vector< CCdCore * > &cds) const
Definition: cuCdFamily.cpp:247
string getNewickRepresentation() const
Definition: cuCdFamily.cpp:537
static void extractFamily(CCdCore *parentCD, CDFamily &cdFamily, vector< CCdCore * > &cds)
Definition: cuCdFamily.cpp:484
CDFamilyIterator setRootCD(CCdCore *rootCD)
Definition: cuCdFamily.cpp:56
CCdCore * m_rootCD
Definition: cuCdFamily.hpp:142
static bool findChildren(CCdCore *cd, vector< CCdCore * > &cds, set< int > &children)
Definition: cuCdFamily.cpp:525
CCdCore * getRootCD() const
Definition: cuCdFamily.cpp:66
static int createFamilies(vector< CCdCore * > &cds, vector< CDFamily * > &families)
Definition: cuCdFamily.cpp:464
CDFamilyIterator findCDByAccession(CCdCore *cd) const
Definition: cuCdFamily.cpp:111
void selectCDs(const vector< CCdCore * > &cds)
Definition: cuCdFamily.cpp:212
void subfamily(CDFamilyIterator cit, CDFamily *&subfam, bool childrenOnly=false)
Definition: cuCdFamily.cpp:187
CDFamilyIterator findCD(CCdCore *cd) const
Definition: cuCdFamily.cpp:99
bool removeChild(CCdCore *cd)
Definition: cuCdFamily.cpp:86
int getSelectedCDs(vector< CCdCore * > &cds)
Definition: cuCdFamily.cpp:231
CDFamilyIterator addChild(CCdCore *cd, CCdCore *parentCD)
Definition: cuCdFamily.cpp:73
bool isDirectAncestor(CCdCore *cd, CCdCore *potentialAncestorCd) const
Definition: cuCdFamily.cpp:382
static bool IsFamilyValid(const CDFamily *family, string &err)
Definition: cuCdFamily.cpp:408
static CDFamily * findFamily(CCdCore *cd, vector< CDFamily > &families)
Definition: cuCdFamily.cpp:452
static bool isDup(CDFamily &one, vector< CDFamily > &all)
Definition: cuCdFamily.cpp:440
void getChildren(vector< CCdCore * > &cds, CCdCore *parentCD) const
Definition: cuCdFamily.cpp:134
void selectAllCDs()
Definition: cuCdFamily.cpp:223
CNcbiOstrstreamToString class helps convert CNcbiOstrstream to a string Sample usage:
Definition: ncbistre.hpp:802
iterator_bool insert(const value_type &val)
Definition: set.hpp:149
const_iterator begin() const
Definition: set.hpp:135
void clear()
Definition: set.hpp:153
size_type size() const
Definition: set.hpp:132
const_iterator find(const key_type &key) const
Definition: set.hpp:137
const_iterator end() const
Definition: set.hpp:136
sibling_iterator end() const
unsigned int number_of_children() const
sibling_iterator begin() const
iter parent(iter) const
Definition: tree_msvc7.hpp:655
iter append_child(iter position)
Definition: tree_msvc7.hpp:680
pre_order_iterator begin() const
Definition: tree_msvc7.hpp:573
iter insert(iter position, const T &x)
Definition: tree_msvc7.hpp:762
int size() const
unsigned int number_of_siblings(const iterator_base &) const
iter erase(iter)
Definition: tree_msvc7.hpp:546
pre_order_iterator end() const
Definition: tree_msvc7.hpp:579
CDFamilyBase::iterator CDFamilyIterator
Definition: cuCdFamily.hpp:57
#define true
Definition: bool.h:35
static char tmp[3200]
Definition: utf8.c:42
#define NULL
Definition: ncbistd.hpp:225
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define END_SCOPE(ns)
End the previously defined scope.
Definition: ncbistl.hpp:75
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
#define BEGIN_SCOPE(ns)
Define a new scope.
Definition: ncbistl.hpp:72
static string UIntToString(unsigned int value, TNumToStringFlags flags=0, int base=10)
Convert UInt to string.
Definition: ncbistr.hpp:5103
static const char label[]
unsigned int
A callback function used to compare two keys in a database.
Definition: types.hpp:1210
where boath are integers</td > n< td ></td > n</tr > n< tr > n< td > tse</td > n< td > optional</td > n< td > String</td > n< td class=\"description\"> TSE option controls what blob is smart and slim</td> n<td> orig</td> n</tr> n<tr> n<td> last_modified</td> n<td> optional</td> n<td> Integer</td> n<td class=\"description\"> The blob last modification If provided then the exact match will be requested with n the Cassandra storage corresponding field value</td> n<td> Positive integer Not provided means that the most recent match will be selected</td> n<td></td> n</tr> n<tr> n<td> use_cache</td> n<td> optional</td> n<td> String</td> n<td class=\"description\"> The option controls if the Cassandra LMDB cache and or database should be used It n affects the seq id resolution step and the blob properties lookup step The following n options are BIOSEQ_INFO and BLOB_PROP at all
int i
yy_size_t n
#define assert(x)
Definition: srv_diag.hpp:58
Modified on Fri Sep 20 14:58:29 2024 by modify_doxy.py rev. 669887