{-# LANGUAGE TemplateHaskell #-}
-- | Defines types and functions to create a chunk database within Drasil.

-- Changes to ChunkDB should be reflected in the 'Creating Your Project 
-- in Drasil' tutorial found on the wiki:
-- https://github.com/JacquesCarette/Drasil/wiki/Creating-Your-Project-in-Drasil
module Database.Drasil.ChunkDB (
  -- * Types
  -- ** 'ChunkDB'
  -- | Main database type
  ChunkDB(symbolTable, termTable, conceptChunkTable, _unitTable, _dataDefnTable,
    _insmodelTable, _gendefTable, _theoryModelTable, _conceptinsTable,
    _citationTable, _labelledcontentTable, _traceTable, _refbyTable, _refTable,
    CDB),
  TermAbbr(..), DomDefn(..),
  -- ** Maps
  -- | Exported for external use.
  RefbyMap, TraceMap, UMap,
  -- * Functions
  -- ** Constructors
  cdb', idMap, symbolMap, termMap, conceptMap, unitMap, traceMap, generateRefbyMap, -- idMap, termMap for docLang
  -- ** Lookup Functions
  asOrderedList, collectUnits,
  termResolve, termResolve', defResolve, symbResolve,
  traceLookup, refbyLookup,
  datadefnLookup, insmodelLookup, gendefLookup, theoryModelLookup,
  conceptinsLookup, refResolve,
  -- ** Lenses
  unitTable, traceTable, refbyTable, citationTable,
  dataDefnTable, insmodelTable, gendefTable, theoryModelTable,
  conceptinsTable, labelledcontentTable, refTable,
  -- **  Helpers
  addCdb, defResolve'
) where

import Language.Drasil (HasUID(..), UID, Quantity, MayHaveUnit(..), Idea (..),
  IdeaDict, Concept, ConceptChunk, IsUnit(..), UnitDefn,
  Reference, ConceptInstance, LabelledContent, Citation,
  nw, cw, unitWrapper, NP, NamedIdea(..), DefinedQuantityDict, dqdWr,
  Sentence, Definition (defn), ConceptDomain (cdom))
import Theory.Drasil (DataDefinition, GenDefn, InstanceModel, TheoryModel)

import Control.Lens ((^.), makeLenses)
import Data.List (sortOn)
import Data.Maybe (fromMaybe, mapMaybe)
import qualified Data.Map as Map
import Utils.Drasil (invert)
import Debug.Trace (trace)

-- | The misnomers below (for the following Map types) are not actually a bad thing. We want to ensure data can't
-- be added to a map if it's not coming from a chunk, and there's no point confusing
-- what the map is for. One is for symbols + their units, and the others are for
-- what they state.
type UMap a = Map.Map UID (a, Int)

-- | A bit of a misnomer as it's really a map of all quantities, for retrieving
-- symbols and their units.
type SymbolMap  = UMap DefinedQuantityDict

-- | A map of all concepts, normally used for retrieving definitions.
type ConceptMap = UMap ConceptChunk

-- | A map of all the units used. Should be restricted to base units/synonyms.
type UnitMap = UMap UnitDefn

-- | Again a bit of a misnomer as it's really a map of all 'NamedIdea's.
-- Until these are built through automated means, there will
-- likely be some 'manual' duplication of terms as this map will contain all
-- quantities, concepts, etc.
type TermMap = UMap IdeaDict
-- | A traceability map, used to hold the relation between one 'UID' and a list of other 'UID's.
type TraceMap = Map.Map UID [UID]
-- | A reference map, used to hold a 'UID' and where it is referenced ('UID's).
type RefbyMap = Map.Map UID [UID]
-- | Data definitions map. Contains all data definitions ('DataDefinition').
type DatadefnMap = UMap DataDefinition
-- | Instance model map. Contains all instance models ('InstanceModel').
type InsModelMap = UMap InstanceModel
-- | General definitions map. Contains all general definitions ('GenDefn').
type GendefMap = UMap GenDefn
-- | Theory model map. Contains all theoretical models ('TheoryModel').
type TheoryModelMap = UMap TheoryModel
-- | Concept instance map. May hold similar information to a 'ConceptMap', but may also be referred to.
type ConceptInstanceMap = UMap ConceptInstance
-- | A map of all 'LabelledContent's.
type LabelledContentMap = UMap LabelledContent
-- | A map of all 'Reference's.
type ReferenceMap = UMap Reference
-- | Citation map.
type CitationMap = UMap Citation

-- | General chunk database map constructor. Creates a 'UMap' from a function
-- that converts something with 'UID's into another type and a list of something
-- with 'UID's.
cdbMap :: HasUID a => String -> (a -> b) -> [a] -> Map.Map UID (b, Int)
cdbMap :: forall a b.
HasUID a =>
String -> (a -> b) -> [a] -> Map UID (b, Int)
cdbMap String
mn a -> b
f [a]
rawChunks = (UID -> (b, Int) -> (b, Int) -> (b, Int))
-> [(UID, (b, Int))] -> Map UID (b, Int)
forall k a. Ord k => (k -> a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWithKey UID -> (b, Int) -> (b, Int) -> (b, Int)
forall {a} {a} {p}. Show a => a -> a -> p -> a
preferNew [(UID, (b, Int))]
kvs
  where
    kvs :: [(UID, (b, Int))]
kvs = (Int -> a -> (UID, (b, Int))) -> [Int] -> [a] -> [(UID, (b, Int))]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (\Int
i a
c -> (a
c a -> Getting UID a UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID a UID
forall c. HasUID c => Getter c UID
Getter a UID
uid, (a -> b
f a
c, Int
i))) [Int
1..] [a]
rawChunks
    preferNew :: a -> a -> p -> a
preferNew a
key a
new p
_ = String -> a -> a
forall a. String -> a -> a
trace (String
"'" String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
key String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"' is inserted twice in '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
mn String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"'!") a
new

-- | Smart constructor for a 'SymbolMap'.
symbolMap :: (Quantity c, MayHaveUnit c, Concept c) => [c] -> SymbolMap
symbolMap :: forall c.
(Quantity c, MayHaveUnit c, Concept c) =>
[c] -> SymbolMap
symbolMap = String -> (c -> DefinedQuantityDict) -> [c] -> SymbolMap
forall a b.
HasUID a =>
String -> (a -> b) -> [a] -> Map UID (b, Int)
cdbMap String
"SymbolMap" c -> DefinedQuantityDict
forall c.
(Quantity c, Concept c, MayHaveUnit c) =>
c -> DefinedQuantityDict
dqdWr

-- | Smart constructor for a 'TermMap'.
termMap :: (Idea c) => [c] -> TermMap
termMap :: forall c. Idea c => [c] -> TermMap
termMap = String -> (c -> IdeaDict) -> [c] -> TermMap
forall a b.
HasUID a =>
String -> (a -> b) -> [a] -> Map UID (b, Int)
cdbMap String
"TermMap" c -> IdeaDict
forall c. Idea c => c -> IdeaDict
nw

-- | Smart constructor for a 'ConceptMap'.
conceptMap :: (Concept c) => [c] -> ConceptMap
conceptMap :: forall c. Concept c => [c] -> ConceptMap
conceptMap = String -> (c -> ConceptChunk) -> [c] -> ConceptMap
forall a b.
HasUID a =>
String -> (a -> b) -> [a] -> Map UID (b, Int)
cdbMap String
"ConceptMap" c -> ConceptChunk
forall c. Concept c => c -> ConceptChunk
cw

-- | Smart constructor for a 'UnitMap'.
unitMap :: (IsUnit u) => [u] -> UnitMap
unitMap :: forall u. IsUnit u => [u] -> UnitMap
unitMap = String -> (u -> UnitDefn) -> [u] -> UnitMap
forall a b.
HasUID a =>
String -> (a -> b) -> [a] -> Map UID (b, Int)
cdbMap String
"UnitMap" u -> UnitDefn
forall u. IsUnit u => u -> UnitDefn
unitWrapper

-- | General smart constructor for making a 'UMap' out of anything that has a 'UID'. 
idMap :: HasUID a => String -> [a] -> Map.Map UID (a, Int)
idMap :: forall a. HasUID a => String -> [a] -> Map UID (a, Int)
idMap String
mn = String -> (a -> a) -> [a] -> Map UID (a, Int)
forall a b.
HasUID a =>
String -> (a -> b) -> [a] -> Map UID (b, Int)
cdbMap String
mn a -> a
forall a. a -> a
id

-- | Smart constructor for a 'TraceMap' given a traceability matrix.
traceMap :: [(UID, [UID])] -> TraceMap
traceMap :: [(UID, [UID])] -> TraceMap
traceMap = [(UID, [UID])] -> TraceMap
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList

-- | Gets a unit if it exists, or Nothing.        
getUnitLup :: HasUID c => ChunkDB -> c -> Maybe UnitDefn
getUnitLup :: forall c. HasUID c => ChunkDB -> c -> Maybe UnitDefn
getUnitLup ChunkDB
m c
c = DefinedQuantityDict -> Maybe UnitDefn
forall u. MayHaveUnit u => u -> Maybe UnitDefn
getUnit (DefinedQuantityDict -> Maybe UnitDefn)
-> DefinedQuantityDict -> Maybe UnitDefn
forall a b. (a -> b) -> a -> b
$ ChunkDB -> UID -> DefinedQuantityDict
symbResolve ChunkDB
m (c
c c -> Getting UID c UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID c UID
forall c. HasUID c => Getter c UID
Getter c UID
uid)

-- | Looks up a 'UID' in a 'UMap' table. If nothing is found, an error is thrown.
uMapLookup :: String -> String -> UID -> UMap a -> a
uMapLookup :: forall a. String -> String -> UID -> UMap a -> a
uMapLookup String
tys String
ms UID
u UMap a
t = Maybe (a, Int) -> a
forall {b} {b}. Maybe (b, b) -> b
getFM (Maybe (a, Int) -> a) -> Maybe (a, Int) -> a
forall a b. (a -> b) -> a -> b
$ UID -> UMap a -> Maybe (a, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
u UMap a
t
  where getFM :: Maybe (b, b) -> b
getFM = b -> ((b, b) -> b) -> Maybe (b, b) -> b
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> b
forall a. HasCallStack => String -> a
error (String -> b) -> String -> b
forall a b. (a -> b) -> a -> b
$ String
tys String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ UID -> String
forall a. Show a => a -> String
show UID
u String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" not found in " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
ms) (b, b) -> b
forall a b. (a, b) -> a
fst

-- | Looks up a 'UID' in the symbol table from the 'ChunkDB'. If nothing is found, an error is thrown.
symbResolve :: ChunkDB -> UID -> DefinedQuantityDict
symbResolve :: ChunkDB -> UID -> DefinedQuantityDict
symbResolve ChunkDB
m UID
x = String -> String -> UID -> SymbolMap -> DefinedQuantityDict
forall a. String -> String -> UID -> UMap a -> a
uMapLookup String
"Symbol" String
"SymbolMap" UID
x (SymbolMap -> DefinedQuantityDict)
-> SymbolMap -> DefinedQuantityDict
forall a b. (a -> b) -> a -> b
$ ChunkDB -> SymbolMap
symbolTable ChunkDB
m

data TermAbbr = TermAbbr { TermAbbr -> NP
longForm :: NP, TermAbbr -> Maybe String
shortForm :: Maybe String }

-- | Search for _any_ chunk that is an instance of 'Idea' and process its "term"
-- and abbreviation.
termResolve :: (NP -> Maybe String -> c) -> ChunkDB -> UID -> c
termResolve :: forall c. (NP -> Maybe String -> c) -> ChunkDB -> UID -> c
termResolve NP -> Maybe String -> c
f ChunkDB
db UID
trg
  | (Just (IdeaDict
c, Int
_)) <- UID -> TermMap -> Maybe (IdeaDict, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
trg (ChunkDB -> TermMap
termTable ChunkDB
db)         = IdeaDict -> c
forall {c}. Idea c => c -> c
go IdeaDict
c
  | (Just (DefinedQuantityDict
c, Int
_)) <- UID -> SymbolMap -> Maybe (DefinedQuantityDict, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
trg (ChunkDB -> SymbolMap
symbolTable ChunkDB
db)       = DefinedQuantityDict -> c
forall {c}. Idea c => c -> c
go DefinedQuantityDict
c
  | (Just (ConceptChunk
c, Int
_)) <- UID -> ConceptMap -> Maybe (ConceptChunk, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
trg (ChunkDB -> ConceptMap
conceptChunkTable ChunkDB
db) = ConceptChunk -> c
forall {c}. Idea c => c -> c
go ConceptChunk
c
  | (Just (UnitDefn
c, Int
_)) <- UID -> UnitMap -> Maybe (UnitDefn, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
trg (ChunkDB -> UnitMap
_unitTable ChunkDB
db)        = UnitDefn -> c
forall {c}. Idea c => c -> c
go UnitDefn
c
  | (Just (DataDefinition
c, Int
_)) <- UID -> Map UID (DataDefinition, Int) -> Maybe (DataDefinition, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
trg (ChunkDB -> Map UID (DataDefinition, Int)
_dataDefnTable ChunkDB
db)    = DataDefinition -> c
forall {c}. Idea c => c -> c
go DataDefinition
c
  | (Just (InstanceModel
c, Int
_)) <- UID -> Map UID (InstanceModel, Int) -> Maybe (InstanceModel, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
trg (ChunkDB -> Map UID (InstanceModel, Int)
_insmodelTable ChunkDB
db)    = InstanceModel -> c
forall {c}. Idea c => c -> c
go InstanceModel
c
  | (Just (GenDefn
c, Int
_)) <- UID -> Map UID (GenDefn, Int) -> Maybe (GenDefn, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
trg (ChunkDB -> Map UID (GenDefn, Int)
_gendefTable ChunkDB
db)      = GenDefn -> c
forall {c}. Idea c => c -> c
go GenDefn
c
  | (Just (TheoryModel
c, Int
_)) <- UID -> Map UID (TheoryModel, Int) -> Maybe (TheoryModel, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
trg (ChunkDB -> Map UID (TheoryModel, Int)
_theoryModelTable ChunkDB
db) = TheoryModel -> c
forall {c}. Idea c => c -> c
go TheoryModel
c
  | (Just (ConceptInstance
c, Int
_)) <- UID
-> Map UID (ConceptInstance, Int) -> Maybe (ConceptInstance, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
trg (ChunkDB -> Map UID (ConceptInstance, Int)
_conceptinsTable ChunkDB
db)  = ConceptInstance -> c
forall {c}. Idea c => c -> c
go ConceptInstance
c
  | Bool
otherwise = String -> c
forall a. HasCallStack => String -> a
error (String -> c) -> String -> c
forall a b. (a -> b) -> a -> b
$ String
"Term: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ UID -> String
forall a. Show a => a -> String
show UID
trg String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" not found in TermMap"
  where go :: c -> c
go c
c = NP -> Maybe String -> c
f (c
c c -> Getting NP c NP -> NP
forall s a. s -> Getting a s a -> a
^. Getting NP c NP
forall c. NamedIdea c => Lens' c NP
Lens' c NP
term) (c -> Maybe String
forall c. Idea c => c -> Maybe String
getA c
c)

-- | Find a chunks "term" and abbreviation, if it exists.
termResolve' :: ChunkDB -> UID -> TermAbbr
termResolve' :: ChunkDB -> UID -> TermAbbr
termResolve' = (NP -> Maybe String -> TermAbbr) -> ChunkDB -> UID -> TermAbbr
forall c. (NP -> Maybe String -> c) -> ChunkDB -> UID -> c
termResolve NP -> Maybe String -> TermAbbr
TermAbbr

-- | Looks up a 'UID' in the reference table from the 'ChunkDB'. If nothing is found, an error is thrown.
refResolve :: UID -> ReferenceMap -> Reference
refResolve :: UID -> ReferenceMap -> Reference
refResolve = String -> String -> UID -> ReferenceMap -> Reference
forall a. String -> String -> UID -> UMap a -> a
uMapLookup String
"Reference" String
"ReferenceMap"

-- | Looks up a 'UID' in the unit table. If nothing is found, an error is thrown.
unitLookup :: UID -> UnitMap -> UnitDefn
unitLookup :: UID -> UnitMap -> UnitDefn
unitLookup = String -> String -> UID -> UnitMap -> UnitDefn
forall a. String -> String -> UID -> UMap a -> a
uMapLookup String
"Unit" String
"UnitMap"

data DomDefn = DomDefn { DomDefn -> [UID]
domain :: [UID], DomDefn -> Sentence
definition :: Sentence}

-- | Looks up a 'UID' in all tables with concepts from the 'ChunkDB'. If nothing is found, an error is thrown.
defResolve :: ([UID] -> Sentence -> c) -> ChunkDB -> UID -> c
defResolve :: forall c. ([UID] -> Sentence -> c) -> ChunkDB -> UID -> c
defResolve [UID] -> Sentence -> c
f ChunkDB
db UID
x
  | (Just (DefinedQuantityDict
c, Int
_)) <- UID -> SymbolMap -> Maybe (DefinedQuantityDict, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
x (ChunkDB -> SymbolMap
symbolTable ChunkDB
db)       = DefinedQuantityDict -> c
forall {s}. (ConceptDomain s, Definition s) => s -> c
go DefinedQuantityDict
c
  | (Just (ConceptChunk
c, Int
_)) <- UID -> ConceptMap -> Maybe (ConceptChunk, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
x (ChunkDB -> ConceptMap
conceptChunkTable ChunkDB
db) = ConceptChunk -> c
forall {s}. (ConceptDomain s, Definition s) => s -> c
go ConceptChunk
c
  | (Just (UnitDefn
c, Int
_)) <- UID -> UnitMap -> Maybe (UnitDefn, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
x (ChunkDB -> UnitMap
_unitTable ChunkDB
db)        = UnitDefn -> c
forall {s}. (ConceptDomain s, Definition s) => s -> c
go UnitDefn
c
  | (Just (InstanceModel
c, Int
_)) <- UID -> Map UID (InstanceModel, Int) -> Maybe (InstanceModel, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
x (ChunkDB -> Map UID (InstanceModel, Int)
_insmodelTable ChunkDB
db)    = InstanceModel -> c
forall {s}. (ConceptDomain s, Definition s) => s -> c
go InstanceModel
c
  | (Just (GenDefn
c, Int
_)) <- UID -> Map UID (GenDefn, Int) -> Maybe (GenDefn, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
x (ChunkDB -> Map UID (GenDefn, Int)
_gendefTable ChunkDB
db)      = GenDefn -> c
forall {s}. (ConceptDomain s, Definition s) => s -> c
go GenDefn
c
  | (Just (TheoryModel
c, Int
_)) <- UID -> Map UID (TheoryModel, Int) -> Maybe (TheoryModel, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
x (ChunkDB -> Map UID (TheoryModel, Int)
_theoryModelTable ChunkDB
db) = TheoryModel -> c
forall {s}. (ConceptDomain s, Definition s) => s -> c
go TheoryModel
c
  | (Just (ConceptInstance
c, Int
_)) <- UID
-> Map UID (ConceptInstance, Int) -> Maybe (ConceptInstance, Int)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
x (ChunkDB -> Map UID (ConceptInstance, Int)
_conceptinsTable ChunkDB
db)  = ConceptInstance -> c
forall {s}. (ConceptDomain s, Definition s) => s -> c
go ConceptInstance
c
  | Bool
otherwise = String -> c
forall a. HasCallStack => String -> a
error (String -> c) -> String -> c
forall a b. (a -> b) -> a -> b
$ String
"Definition: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ UID -> String
forall a. Show a => a -> String
show UID
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" not found in ConceptMap"
  where go :: s -> c
go s
c = [UID] -> Sentence -> c
f (s -> [UID]
forall c. ConceptDomain c => c -> [UID]
cdom s
c) (s
c s -> Getting Sentence s Sentence -> Sentence
forall s a. s -> Getting a s a -> a
^. Getting Sentence s Sentence
forall c. Definition c => Lens' c Sentence
Lens' s Sentence
defn)

defResolve' :: ChunkDB -> UID -> DomDefn
defResolve' :: ChunkDB -> UID -> DomDefn
defResolve' = ([UID] -> Sentence -> DomDefn) -> ChunkDB -> UID -> DomDefn
forall c. ([UID] -> Sentence -> c) -> ChunkDB -> UID -> c
defResolve [UID] -> Sentence -> DomDefn
DomDefn

-- | Looks up a 'UID' in the datadefinition table. If nothing is found, an error is thrown.
datadefnLookup :: UID -> DatadefnMap -> DataDefinition
datadefnLookup :: UID -> Map UID (DataDefinition, Int) -> DataDefinition
datadefnLookup = String
-> String -> UID -> Map UID (DataDefinition, Int) -> DataDefinition
forall a. String -> String -> UID -> UMap a -> a
uMapLookup String
"DataDefinition" String
"DatadefnMap"

-- | Looks up a 'UID' in the instance model table. If nothing is found, an error is thrown.
insmodelLookup :: UID -> InsModelMap -> InstanceModel
insmodelLookup :: UID -> Map UID (InstanceModel, Int) -> InstanceModel
insmodelLookup = String
-> String -> UID -> Map UID (InstanceModel, Int) -> InstanceModel
forall a. String -> String -> UID -> UMap a -> a
uMapLookup String
"InstanceModel" String
"InsModelMap"

-- | Looks up a 'UID' in the general definition table. If nothing is found, an error is thrown.
gendefLookup :: UID -> GendefMap -> GenDefn
gendefLookup :: UID -> Map UID (GenDefn, Int) -> GenDefn
gendefLookup = String -> String -> UID -> Map UID (GenDefn, Int) -> GenDefn
forall a. String -> String -> UID -> UMap a -> a
uMapLookup String
"GenDefn" String
"GenDefnMap"

-- | Looks up a 'UID' in the theory model table. If nothing is found, an error is thrown.
theoryModelLookup :: UID -> TheoryModelMap -> TheoryModel
theoryModelLookup :: UID -> Map UID (TheoryModel, Int) -> TheoryModel
theoryModelLookup = String
-> String -> UID -> Map UID (TheoryModel, Int) -> TheoryModel
forall a. String -> String -> UID -> UMap a -> a
uMapLookup String
"TheoryModel" String
"TheoryModelMap"

-- | Looks up a 'UID' in the concept instance table. If nothing is found, an error is thrown.
conceptinsLookup :: UID -> ConceptInstanceMap -> ConceptInstance
conceptinsLookup :: UID -> Map UID (ConceptInstance, Int) -> ConceptInstance
conceptinsLookup = String
-> String
-> UID
-> Map UID (ConceptInstance, Int)
-> ConceptInstance
forall a. String -> String -> UID -> UMap a -> a
uMapLookup String
"ConceptInstance" String
"ConceptInstanceMap"

-- | Gets an ordered list of @a@ from any @a@ that is of type 'UMap'.
asOrderedList :: UMap a -> [a]
asOrderedList :: forall a. UMap a -> [a]
asOrderedList = ((a, Int) -> a) -> [(a, Int)] -> [a]
forall a b. (a -> b) -> [a] -> [b]
map (a, Int) -> a
forall a b. (a, b) -> a
fst ([(a, Int)] -> [a]) -> (UMap a -> [(a, Int)]) -> UMap a -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((a, Int) -> Int) -> [(a, Int)] -> [(a, Int)]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (a, Int) -> Int
forall a b. (a, b) -> b
snd ([(a, Int)] -> [(a, Int)])
-> (UMap a -> [(a, Int)]) -> UMap a -> [(a, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((UID, (a, Int)) -> (a, Int)) -> [(UID, (a, Int))] -> [(a, Int)]
forall a b. (a -> b) -> [a] -> [b]
map (UID, (a, Int)) -> (a, Int)
forall a b. (a, b) -> b
snd ([(UID, (a, Int))] -> [(a, Int)])
-> (UMap a -> [(UID, (a, Int))]) -> UMap a -> [(a, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UMap a -> [(UID, (a, Int))]
forall k a. Map k a -> [(k, a)]
Map.toList

-- | Our chunk databases. \Must contain all maps needed in an example.\
-- In turn, these maps must contain every chunk definition or concept 
-- used in its respective example, else an error is thrown.
data ChunkDB = CDB {
  -- CHUNKS
    ChunkDB -> SymbolMap
symbolTable           :: SymbolMap
  , ChunkDB -> TermMap
termTable             :: TermMap
  , ChunkDB -> ConceptMap
conceptChunkTable     :: ConceptMap
  , ChunkDB -> UnitMap
_unitTable            :: UnitMap
  , ChunkDB -> Map UID (DataDefinition, Int)
_dataDefnTable        :: DatadefnMap
  , ChunkDB -> Map UID (InstanceModel, Int)
_insmodelTable        :: InsModelMap
  , ChunkDB -> Map UID (GenDefn, Int)
_gendefTable          :: GendefMap
  , ChunkDB -> Map UID (TheoryModel, Int)
_theoryModelTable     :: TheoryModelMap
  , ChunkDB -> Map UID (ConceptInstance, Int)
_conceptinsTable      :: ConceptInstanceMap
  , ChunkDB -> CitationMap
_citationTable        :: CitationMap
  -- NOT CHUNKS
  , ChunkDB -> LabelledContentMap
_labelledcontentTable :: LabelledContentMap -- TODO: LabelledContent needs to be rebuilt. See JacquesCarette/Drasil#4023.
  , ChunkDB -> ReferenceMap
_refTable             :: ReferenceMap -- TODO: References need to be rebuilt. See JacquesCarette/Drasil#4022.
  , ChunkDB -> TraceMap
_traceTable           :: TraceMap
  , ChunkDB -> TraceMap
_refbyTable           :: RefbyMap
  }
makeLenses ''ChunkDB

-- | Smart constructor for chunk databases. Takes in the following:
--
--     * ['Quantity'] (for 'SymbolMap'), 
--     * 'NamedIdea's (for 'TermMap'),
--     * 'Concept's (for 'ConceptMap'),
--     * Units (something that 'IsUnit' for 'UnitMap'),
--     * 'DataDefinition's (for 'DatadefnMap'),
--     * 'InstanceModel's (for 'InsModelMap'),
--     * 'GenDefn's (for 'GendefMap'),
--     * 'TheoryModel's (for 'TheoryModelMap'),
--     * 'ConceptInstance's (for 'ConceptInstanceMap'),
--     * 'LabelledContent's (for 'LabelledContentMap').
-- Creates an empty ChunkDB without basic data. cdb in MetaDatabase.Drasil
-- should be preferred over this function as it includes the basic data needed
-- for Drasil.
cdb' :: (Quantity q, MayHaveUnit q, Concept q, Concept c, IsUnit u) =>
    [q] -> [IdeaDict] -> [c] -> [u] -> [DataDefinition] -> [InstanceModel] ->
    [GenDefn] -> [TheoryModel] -> [ConceptInstance] ->
    [LabelledContent] -> [Reference] -> [Citation] -> ChunkDB
cdb' :: forall q c u.
(Quantity q, MayHaveUnit q, Concept q, Concept c, IsUnit u) =>
[q]
-> [IdeaDict]
-> [c]
-> [u]
-> [DataDefinition]
-> [InstanceModel]
-> [GenDefn]
-> [TheoryModel]
-> [ConceptInstance]
-> [LabelledContent]
-> [Reference]
-> [Citation]
-> ChunkDB
cdb' [q]
s [IdeaDict]
t [c]
c [u]
u [DataDefinition]
d [InstanceModel]
ins [GenDefn]
gd [TheoryModel]
tm [ConceptInstance]
ci [LabelledContent]
lc [Reference]
r [Citation]
cits =
  CDB {
    -- CHUNKS
    symbolTable :: SymbolMap
symbolTable = [q] -> SymbolMap
forall c.
(Quantity c, MayHaveUnit c, Concept c) =>
[c] -> SymbolMap
symbolMap [q]
s,
    termTable :: TermMap
termTable = [IdeaDict] -> TermMap
forall c. Idea c => [c] -> TermMap
termMap [IdeaDict]
t,
    conceptChunkTable :: ConceptMap
conceptChunkTable = [c] -> ConceptMap
forall c. Concept c => [c] -> ConceptMap
conceptMap [c]
c,
    _unitTable :: UnitMap
_unitTable = [u] -> UnitMap
forall u. IsUnit u => [u] -> UnitMap
unitMap [u]
u,
    _dataDefnTable :: Map UID (DataDefinition, Int)
_dataDefnTable = String -> [DataDefinition] -> Map UID (DataDefinition, Int)
forall a. HasUID a => String -> [a] -> Map UID (a, Int)
idMap String
"DataDefnMap" [DataDefinition]
d,
    _insmodelTable :: Map UID (InstanceModel, Int)
_insmodelTable = String -> [InstanceModel] -> Map UID (InstanceModel, Int)
forall a. HasUID a => String -> [a] -> Map UID (a, Int)
idMap String
"InsModelMap" [InstanceModel]
ins,
    _gendefTable :: Map UID (GenDefn, Int)
_gendefTable = String -> [GenDefn] -> Map UID (GenDefn, Int)
forall a. HasUID a => String -> [a] -> Map UID (a, Int)
idMap String
"GenDefnmap" [GenDefn]
gd,
    _theoryModelTable :: Map UID (TheoryModel, Int)
_theoryModelTable = String -> [TheoryModel] -> Map UID (TheoryModel, Int)
forall a. HasUID a => String -> [a] -> Map UID (a, Int)
idMap String
"TheoryModelMap" [TheoryModel]
tm,
    _conceptinsTable :: Map UID (ConceptInstance, Int)
_conceptinsTable = String -> [ConceptInstance] -> Map UID (ConceptInstance, Int)
forall a. HasUID a => String -> [a] -> Map UID (a, Int)
idMap String
"ConcInsMap" [ConceptInstance]
ci,
    _citationTable :: CitationMap
_citationTable = String -> [Citation] -> CitationMap
forall a. HasUID a => String -> [a] -> Map UID (a, Int)
idMap String
"CiteMap" [Citation]
cits,
    -- NOT CHUNKS
    _labelledcontentTable :: LabelledContentMap
_labelledcontentTable = String -> [LabelledContent] -> LabelledContentMap
forall a. HasUID a => String -> [a] -> Map UID (a, Int)
idMap String
"LLCMap" [LabelledContent]
lc,
    _traceTable :: TraceMap
_traceTable = TraceMap
forall k a. Map k a
Map.empty,
    _refbyTable :: TraceMap
_refbyTable = TraceMap
forall k a. Map k a
Map.empty,
    _refTable :: ReferenceMap
_refTable = String -> [Reference] -> ReferenceMap
forall a. HasUID a => String -> [a] -> Map UID (a, Int)
idMap String
"RefMap" [Reference]
r
  }


addCdb :: ChunkDB -> ChunkDB -> ChunkDB
addCdb :: ChunkDB -> ChunkDB -> ChunkDB
addCdb ChunkDB
cdb1 ChunkDB
cdb2 =
  CDB {
    -- CHUNKS
    symbolTable :: SymbolMap
symbolTable           = String -> SymbolMap -> SymbolMap -> SymbolMap
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"SymbolMap" (ChunkDB -> SymbolMap
symbolTable ChunkDB
cdb1) (ChunkDB -> SymbolMap
symbolTable ChunkDB
cdb2),
    termTable :: TermMap
termTable             = String -> TermMap -> TermMap -> TermMap
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"TermMap" (ChunkDB -> TermMap
termTable ChunkDB
cdb1) (ChunkDB -> TermMap
termTable ChunkDB
cdb2),
    conceptChunkTable :: ConceptMap
conceptChunkTable     = String -> ConceptMap -> ConceptMap -> ConceptMap
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"ConceptMap" (ChunkDB -> ConceptMap
conceptChunkTable ChunkDB
cdb1) (ChunkDB -> ConceptMap
conceptChunkTable ChunkDB
cdb2),
    _unitTable :: UnitMap
_unitTable            = String -> UnitMap -> UnitMap -> UnitMap
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"UnitMap" (ChunkDB -> UnitMap
_unitTable ChunkDB
cdb1) (ChunkDB -> UnitMap
_unitTable ChunkDB
cdb2),
    _dataDefnTable :: Map UID (DataDefinition, Int)
_dataDefnTable        = String
-> Map UID (DataDefinition, Int)
-> Map UID (DataDefinition, Int)
-> Map UID (DataDefinition, Int)
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> Map UID (DataDefinition, Int)
_dataDefnTable ChunkDB
cdb1) (ChunkDB -> Map UID (DataDefinition, Int)
_dataDefnTable ChunkDB
cdb2),
    _insmodelTable :: Map UID (InstanceModel, Int)
_insmodelTable        = String
-> Map UID (InstanceModel, Int)
-> Map UID (InstanceModel, Int)
-> Map UID (InstanceModel, Int)
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> Map UID (InstanceModel, Int)
_insmodelTable ChunkDB
cdb1) (ChunkDB -> Map UID (InstanceModel, Int)
_insmodelTable ChunkDB
cdb2),
    _gendefTable :: Map UID (GenDefn, Int)
_gendefTable          = String
-> Map UID (GenDefn, Int)
-> Map UID (GenDefn, Int)
-> Map UID (GenDefn, Int)
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> Map UID (GenDefn, Int)
_gendefTable ChunkDB
cdb1) (ChunkDB -> Map UID (GenDefn, Int)
_gendefTable ChunkDB
cdb2),
    _theoryModelTable :: Map UID (TheoryModel, Int)
_theoryModelTable     = String
-> Map UID (TheoryModel, Int)
-> Map UID (TheoryModel, Int)
-> Map UID (TheoryModel, Int)
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> Map UID (TheoryModel, Int)
_theoryModelTable ChunkDB
cdb1) (ChunkDB -> Map UID (TheoryModel, Int)
_theoryModelTable ChunkDB
cdb2),
    _conceptinsTable :: Map UID (ConceptInstance, Int)
_conceptinsTable      = String
-> Map UID (ConceptInstance, Int)
-> Map UID (ConceptInstance, Int)
-> Map UID (ConceptInstance, Int)
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> Map UID (ConceptInstance, Int)
_conceptinsTable ChunkDB
cdb1) (ChunkDB -> Map UID (ConceptInstance, Int)
_conceptinsTable ChunkDB
cdb2),
    _citationTable :: CitationMap
_citationTable        = String -> CitationMap -> CitationMap -> CitationMap
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> CitationMap
_citationTable ChunkDB
cdb1) (ChunkDB -> CitationMap
_citationTable ChunkDB
cdb2),
    -- NOT CHUNKS
    _labelledcontentTable :: LabelledContentMap
_labelledcontentTable = String
-> LabelledContentMap -> LabelledContentMap -> LabelledContentMap
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> LabelledContentMap
_labelledcontentTable ChunkDB
cdb1) (ChunkDB -> LabelledContentMap
_labelledcontentTable ChunkDB
cdb2),
    _traceTable :: TraceMap
_traceTable           = String -> TraceMap -> TraceMap -> TraceMap
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> TraceMap
_traceTable ChunkDB
cdb1) (ChunkDB -> TraceMap
_traceTable ChunkDB
cdb2),
    _refbyTable :: TraceMap
_refbyTable           = String -> TraceMap -> TraceMap -> TraceMap
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> TraceMap
_refbyTable ChunkDB
cdb1) (ChunkDB -> TraceMap
_refbyTable ChunkDB
cdb2),
    _refTable :: ReferenceMap
_refTable             = String -> ReferenceMap -> ReferenceMap -> ReferenceMap
forall {k} {a}.
(Ord k, Show k) =>
String -> Map k a -> Map k a -> Map k a
concatCdbMap String
"" (ChunkDB -> ReferenceMap
_refTable ChunkDB
cdb1) (ChunkDB -> ReferenceMap
_refTable ChunkDB
cdb2)
  }
  where
    concatCdbMap :: String -> Map k a -> Map k a -> Map k a
concatCdbMap String
mn = (k -> a -> a -> a) -> Map k a -> Map k a -> Map k a
forall k a.
Ord k =>
(k -> a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWithKey (String -> k -> a -> a -> a
forall {a} {a} {p}. Show a => String -> a -> a -> p -> a
preferNew String
mn) 
    preferNew :: String -> a -> a -> p -> a
preferNew String
mn a
key a
new p
_ = String -> a -> a
forall a. String -> a -> a
trace (String
"'" String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
key String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"' is inserted twice in '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
mn String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"' while adding to basis!") a
new

-- | Gets the units of a 'Quantity' as 'UnitDefn's.
collectUnits :: Quantity c => ChunkDB -> [c] -> [UnitDefn]
collectUnits :: forall c. Quantity c => ChunkDB -> [c] -> [UnitDefn]
collectUnits ChunkDB
m = (UID -> UnitDefn) -> [UID] -> [UnitDefn]
forall a b. (a -> b) -> [a] -> [b]
map (UnitDefn -> UnitDefn
forall u. IsUnit u => u -> UnitDefn
unitWrapper (UnitDefn -> UnitDefn) -> (UID -> UnitDefn) -> UID -> UnitDefn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UID -> UnitMap -> UnitDefn) -> UnitMap -> UID -> UnitDefn
forall a b c. (a -> b -> c) -> b -> a -> c
flip UID -> UnitMap -> UnitDefn
unitLookup (ChunkDB
m ChunkDB -> Getting UnitMap ChunkDB UnitMap -> UnitMap
forall s a. s -> Getting a s a -> a
^. Getting UnitMap ChunkDB UnitMap
Lens' ChunkDB UnitMap
unitTable))
 ([UID] -> [UnitDefn]) -> ([c] -> [UID]) -> [c] -> [UnitDefn]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UnitDefn -> [UID]) -> [UnitDefn] -> [UID]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap UnitDefn -> [UID]
forall u. IsUnit u => u -> [UID]
getUnits ([UnitDefn] -> [UID]) -> ([c] -> [UnitDefn]) -> [c] -> [UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (c -> Maybe UnitDefn) -> [c] -> [UnitDefn]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (ChunkDB -> c -> Maybe UnitDefn
forall c. HasUID c => ChunkDB -> c -> Maybe UnitDefn
getUnitLup ChunkDB
m)

-- | Trace a 'UID' to related 'UID's.
traceLookup :: UID -> TraceMap -> [UID]
traceLookup :: UID -> TraceMap -> [UID]
traceLookup UID
c = [UID] -> Maybe [UID] -> [UID]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [UID] -> [UID])
-> (TraceMap -> Maybe [UID]) -> TraceMap -> [UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UID -> TraceMap -> Maybe [UID]
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
c

-- | Translates a traceability map into a reference map.
generateRefbyMap :: TraceMap -> RefbyMap
generateRefbyMap :: TraceMap -> TraceMap
generateRefbyMap = TraceMap -> TraceMap
forall v k. Ord v => Map k [v] -> Map v [k]
invert

-- | Trace a 'UID' to referenced 'UID's.
refbyLookup :: UID -> RefbyMap -> [UID]
refbyLookup :: UID -> TraceMap -> [UID]
refbyLookup UID
c = [UID] -> Maybe [UID] -> [UID]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [UID] -> [UID])
-> (TraceMap -> Maybe [UID]) -> TraceMap -> [UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UID -> TraceMap -> Maybe [UID]
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup UID
c