1 /*
2  *  Copyright (C) 2004-2021 Edward F. Valeev
3  *
4  *  This file is part of Libint.
5  *
6  *  Libint is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  Libint is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with Libint.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #ifndef _libint2_src_bin_libint_singlstack_h_
22 #define _libint2_src_bin_libint_singlstack_h_
23 
24 #include <map>
25 #include <iostream>
26 #include <smart_ptr.h>
27 #include <key.h>
28 #include <hashable.h>
29 #include <purgeable.h>
30 
31 namespace libint2 {
32 
33   class RecurrenceRelation;
34 
35   /**
36      SingletonStack<T,KeyType> helps to implement Singleton-like objects of type T.
37      SingletonStack maintains a map of keys of type KeyType to
38      smart pointers to objects of type T. Keys are computed from T by
39      calling a callback of type HashingFunction passed to the constructor to SingletonStack -- hence
40      HashingFunction is a member function T which takes no arguments and returns a const KeyType&.
41    */
42   template <class T, class KeyType>
43   class SingletonStack : public PurgeableStack<T>
44   {
45   public:
46     typedef KeyType key_type;
47     typedef SafePtr<T> data_type;
48     /// Specifies type for the instance index variables
49     typedef KeyTypes::InstanceID InstanceID;
50     typedef std::pair<InstanceID,SafePtr<T> > value_type;
51     typedef std::map<key_type,value_type> map_type;
52     /// use iter_type objects to iterate over the stack
53     typedef typename map_type::iterator iter_type;
54     /// const version of iter_type
55     typedef typename map_type::const_iterator citer_type;
56     /// hashing function returns keys as key_return_type
57     typedef typename KeyTraits<key_type>::ReturnType key_return_type;
58     /// Specifies the type of callback which computes hashes
59     typedef key_return_type (T::* HashingFunction)() const;
60     /// PurgingPolicy determines whether and which objects on this stack are obsolete and can be removed
61     typedef typename PurgeableStack<T>::PurgingPolicy PurgingPolicy;
62 
63 
64     /// callback to compute hash values is the only parameter
SingletonStack(HashingFunction callback)65     SingletonStack(HashingFunction callback) :
66       map_(), callback_(callback), next_instance_(0)
67     {
68       if (PurgingPolicy::purgeable()) { // if this stack contains objects that can be purged, add to the registry
69         PurgeableStacks::Instance()->register_stack(this);
70       }
71     }
72 
~SingletonStack()73     virtual ~SingletonStack() {}
74 
75     /** Returns the pointer to the unique instance of object obj.
76           find() computes obj->*callback_(), searches it in hstack_,
77           if found -- returns the pointer to the corresponding object on ostack_,
78           otherwise pushes obj to the end of ostack_ and returns obj.
79      */
find(const SafePtr<T> & obj)80     const value_type& find(const SafePtr<T>& obj) {
81       key_type key = ((obj.get())->*callback_)();
82 
83       typedef typename map_type::iterator miter;
84       miter pos = map_.find(key);
85       if (pos != map_.end()) {
86 #if DEBUG || LOCAL_DEBUG
87         std::cout << "SingletonStack::find -- " << obj->label() << " already found" << std::endl;
88 #endif
89         return (*pos).second;
90       }
91       else {
92         value_type result(next_instance_++,obj);
93         map_[key] = result;
94 #if DEBUG || LOCAL_DEBUG
95         std::cout << "SingletonStack::find -- " << obj->label() << " is new (instid_ = " << next_instance_-1 << ")" << std::endl;
96 #endif
97         return map_[key];
98       }
99     }
100 
101     /** Returns the pointer to the unique instance of object corresponding to key.
102           if found returns the pointer to the corresponding object on ostack_,
103           else returns a null value_type.
104      */
find(const key_type & key)105     const value_type& find(const key_type& key) {
106       static value_type null_value(make_pair(InstanceID(0),SafePtr<T>()));
107       typedef typename map_type::iterator miter;
108       miter pos = map_.find(key);
109       if (pos != map_.end()) {
110         return (*pos).second;
111       }
112       else {
113         return null_value;
114       }
115     }
116 
117     /** Returns the pointer to the unique instance of object corresponding to the hashed_key.
118           if found returns the pointer to the corresponding object on ostack_,
119           else returns a null value_type.
120      */
find_hashed(const InstanceID & hashed_key)121     const value_type& find_hashed(const InstanceID& hashed_key) const {
122       static value_type null_value(make_pair(InstanceID(0),SafePtr<T>()));
123       for(auto& i: map_) {
124         if (i.second.first == hashed_key)
125           return i.second;
126       }
127       return null_value;
128     }
129 
130     /** Searches for obj on the stack and, if found, removes the unique instance
131      */
remove(const SafePtr<T> & obj)132     void remove(const SafePtr<T>& obj) {
133       key_type key = ((obj.get())->*callback_)();
134 
135       typedef typename map_type::iterator miter;
136       miter pos = map_.find(key);
137       if (pos != map_.end()) {
138         map_.erase(pos);
139 #if DEBUG || LOCAL_DEBUG
140         std::cout << "Removed from stack " << obj->label() << std::endl;
141 #endif
142       }
143     }
144 
145     /** Returns iterator to the beginning of the stack */
begin()146     citer_type begin() const { return map_.begin(); }
147     /** Returns iterator to the end of the stack */
end()148     citer_type end() const { return map_.end(); }
149 
150     // Implementation of PurgeableStack::purge()
purge()151     void purge() override {
152       for(iter_type i = map_.begin(); i!=map_.end();) {
153         const T* v = i->second.second.get();
154         if (PurgingPolicy::purge(v))
155           // map::erase invalidates the iterator, increment it beforehand
156           map_.erase(i++);
157         else
158           ++i;
159       }
160     }
161 
162   private:
163     map_type map_;
164     HashingFunction callback_;
165     InstanceID next_instance_;
166   };
167 
168 };
169 
170 #endif
171