
Author: Anne Maarsel
This specification is for a bibliography database. It has been written
by Anne Maarsel when she was a student working at OECD Halden Reator
Project in the Summer of 1998. Note that this specification can be
improved in various ways. First of all it introduces post-conditions
that are not really adding much value since they are virtually
identical to the explicit bodies. In addition the use of an optional
type instead of 
| Properties | Values | 
|---|---|
| Language Version: | classic | 
| Entry point : | DEFAULT`isnumber(42) | 
types
Id = nat;
String = seq of char|<nil>;
Edition = nat|<nil>
inv e == e in set {1,...,50} union {<nil>};
Month = nat|<nil>
inv e == e in set {1,...,12} union {<nil>};
Number = nat1|<nil>;
Pages = nat1|<nil>;
Series = nat1|<nil>;
Volume = nat1|<nil>;
Year = nat|<nil>
inv e == e in set {1800,...,1998} union {<nil>};
Article::id	:Id
	author	:String
	journal	:String
	month	:Month
	note	:String
	number	:Number
	pages	:Pages
	title	:String
	volume	:Volume	
	year	:Year;
Book::	id	:Id
	address	:String
	author	:String
	edition	:Edition
	editor	:String
	month	:Month
	note	:String
	publisher:String
	series	:Series
	title	:String
	volume	:Volume	
	year	:Year;
Inproceeding::	id	:Id
		address	:String
		author	:String
		booktitle:String
		editor	:String
		month	:Month
		note	:String
		organization:String
		pages	:Pages
		publisher:String
		title	:String
		year	:Year;	
Manual::id	:Id
	address	:String
	author	:String
	edition	:Edition
	month	:Month
	note	:String
	organization:String
	title	:String
	year	:Year;
Techreport::	id	:Id
		address	:String
		author	:String
		institution:String
		month	:Month
		note	:String
		number	:Number
		title	:String
		type	:String
		year	:Year;
Record =  Article | Book | Inproceeding | Manual | Techreport;
Recordtype = <article> | <book> | <inproceeding> | <manual> | <techreport>;
Value = Id | String | Edition | Month | Number | Pages | 
	Series | Volume | Year;
Valuetype = <id> | <address> | <author> | <booktitle> | <edition> | 
	<editor> | <institution> | <journal> | <month> | <note> | 
	<number> | <organization> | <pages> | <publisher> | <series> | 
	<title> | <type> | <volume> | <year>;
state mgd of
dB:set of Record
init dB==dB=mk_mgd({})
end
functions
field: Recordtype +> set of Valuetype
field(rt)==
  required(rt) union optional(rt)
post RESULT=required(rt) union optional(rt);
get: set of Record*Id +> Record
get(dB,i)==
  let record in set dB 
  in 
	if record.id=i 
	then record 
	else get(dB\{record},i)	
pre	i in set usedIds(dB) 
post RESULT.id=i and RESULT in set dB
measure card db;
  
getvalue: Valuetype*set of Record*Id +> Value
getvalue(valuetype,dB,i)==
	cases valuetype:
		<title>	->		get(dB,i).title,
		<author> ->		get(dB,i).author,
		<journal> ->		get(dB,i).journal,
		<year> ->		get(dB,i).year,
		<booktitle> ->		get(dB,i).booktitle,
		<institution> ->	get(dB,i).institution,
		<publisher> ->		get(dB,i).publisher
	end
pre	i in set usedIds(dB) and
	valuetype in set {<title>,<author>,<journal>,<year>,<booktitle>,
			<institution>,<publisher>}
post 	valuetype=<title>	and RESULT=get(dB,i).title or
	valuetype=<author>	and RESULT=get(dB,i).author or
	valuetype=<journal>	and RESULT=get(dB,i).journal or 
	valuetype=<year>	and RESULT=get(dB,i).year or
	valuetype=<booktitle>	and RESULT=get(dB,i).booktitle or
	valuetype=<institution>	and RESULT=get(dB,i).institution or
	valuetype=<publisher>	and RESULT=get(dB,i).publisher;
iscomplete :set of Record*Id +> bool
iscomplete(dB,i)==
  required(recordtype(dB,i))={f|f in set required(recordtype(dB,i)) 
                               & not isempty(getvalue(f,dB,i))}
pre	i in set usedIds(dB) 
post (forall x in set required(recordtype(dB,i)) & 
	     not isempty(getvalue(x,dB,i))) <=> RESULT;
isedition: Value +> bool
isedition(v)==
	v in set {1,...,50} or v=<nil>;
isempty: Value +> bool
isempty(value)==
	if value=<nil> then true
	else false
post value=<nil> <=> RESULT;
isidentical: set of Record +> bool
isidentical(dB)==
	if dB={} then false 
	else let record1 in set dB in 
	if iscomplete(dB,record1.id) 
	then isidentical2(dB,dB\{record1},dB,record1)
	else isidentical(dB\{record1})
post 	(exists i,j in set usedIds(dB) & i<>j and iscomplete(dB,i) 
	and iscomplete (dB,j) 
	and recordtype(dB,i)=recordtype(dB,j) and
	forall x in set required(recordtype(dB,i)) 
	& getvalue(x,dB,i)=getvalue(x,dB,j)) <=> RESULT;
isidentical2: set of Record*set of Record*set of Record*Record +>bool
isidentical2(dB1,dB2,olddB,record1)==
	if dB2={} then isidentical(dB1\{record1}) 
	else let record2 in set dB2 in
	if iscomplete(olddB,record2.id) then
	isidentical3(dB1,dB2,olddB,record1,record2,
	required(recordtype(olddB,record1.id)))
	else isidentical2(dB1,dB2\{record2},olddB,record1);
isidentical3: set of Record*set of Record*set of Record*Record*
              Record*set of Valuetype +> bool
isidentical3(dB1,dB2,olddB,record1,record2,requiredfields)==
	if requiredfields={} then true
	else let field in set requiredfields in
	if getvalue(field,olddB,record1.id)<>
	getvalue(field,olddB,record2.id) 
	then isidentical2(dB1,dB2\{record2},olddB,record1)
	else isidentical3(dB1,dB2,olddB,record1,record2,requiredfields\{field});
ismonth: Value +> bool
ismonth(v)==
	is_nat(v);
	--v in set {1,...,12} or v=<nil>;
isnumber: Value +> bool
isnumber(v)==
	is_nat(v) or v=<nil>;
ispages: Value +> bool
ispages(v)==
	is_nat(v) or v=<nil>;
isstring: Value +> bool
isstring(v)==
  if not is_real(v) 
  then if v=[] 
       then true
       elseif is_char(hd(v)) 
       then isstring(tl(v))
       else false
  else false;
	
issubstring: String*String +> bool
issubstring(string1,string2)==
	if string1=[] then true 
	elseif string2=[] or string1=<nil> or string2=<nil> then false
	elseif hd(string1)=hd(string2) 
	then issubstring2(tl(string1),tl(string2),string1)
	else issubstring(string1,tl(string2))
post	(not (string2=<nil>)) and (exists i,j in set inds(string2) 
	& substring(string2,i,j)=string1) <=> RESULT;
issubstring2: String*String*String +> bool
issubstring2(string1,string2,oldstring1)==
	if string1=[] then true 
	elseif string2=[] then false
	elseif hd(string1)=hd(string2) 
	then issubstring2(tl(string1),tl(string2),oldstring1)
	else issubstring(oldstring1,string2);
isvalueoffield: Value*Valuetype +> bool
isvalueoffield(v,f)==
	cases f:
		<address> ->	isstring(v),
		<author> ->	isstring(v), 
		<booktitle> ->	isstring(v),
		<edition> ->	isedition(v),
		<editor> ->	isstring(v),
		<institution> ->isstring(v),
		<journal> ->	isstring(v), 
		<month>	->	ismonth(v),
		<note> ->	isstring(v),
		<number> ->	isnumber(v),
		<organization> ->isstring(v),
		<pages> ->	ispages(v),
		<publisher> ->	isstring(v),
		<title>	->	isstring(v),
		<type> ->	isstring(v),
		<volume> ->	isvolume(v),
		<year> ->	isyear(v)
	end
post 	((f=<address> 	and exists x:String & x=v) or
	(f=<author> 	and exists x:String & x=v) or
	(f=<booktitle> 	and exists x:String & x=v) or
	(f=<edition> 	and exists x:Edition & x=v) or
	(f=<editor> 	and exists x:String & x=v) or
	(f=<institution> and exists x:String & x=v) or
	(f=<journal> 	and exists x:String & x=v) or
	(f=<month> 	and exists x:Month & x=v) or
	(f=<note> 	and exists x:String & x=v) or
	(f=<number> 	and exists x:Number & x=v) or
	(f=<organization>and exists x:String & x=v) or
	(f=<pages> 	and exists x:Pages & x=v) or
	(f=<publisher> 	and exists x:String & x=v) or
	(f=<title> 	and exists x:String & x=v) or
	(f=<type> 	and exists x:String & x=v) or
	(f=<volume> 	and exists x:Volume & x=v) or
	(f=<year> 	and exists x:Year & x=v)) <=> RESULT;
isvolume: Value +> bool
isvolume(v)==
	is_nat(v) or v=<nil>;
isyear: Value +> bool
isyear(v)==
	v in set {1800,...,1998} or v=<nil>;
optional: Recordtype +> set of Valuetype
optional(rt)==
	cases rt:
		<article> 	-> {<volume>,<number>,<month>,<note>},
		<book>		-> {<volume>,<series>,<address>,
				<edition>,<month>,<note>,<publisher>},
		<inproceeding>	-> {<editor>,<pages>,<organization>,
				<publisher>,<address>,<pages>,<organization>},
		<manual>	-> {<edition>,<note>,<organization>,<month>,
				<address>,<author>,<organization>,<year>},
		<techreport>	-> {<number>,<note>,<type>,<month>,<address>}
	end
post 	rt = <article>		
	and RESULT={<volume>,<number>,<month>,<note>} or
	rt = <book>		
	and RESULT={<volume>,<series>,<address>,<edition>,<month>,<note>,
		<publisher>} or
 	rt = <inproceeding>	
	and RESULT={<editor>,<pages>,<organization>,<publisher>,<address>,
		<pages>,<organization>} or
	rt = <manual>		
	and RESULT={<edition>,<note>,<organization>,<month>,<address>,
		<author>,<organization>,<year>} or
	rt = <techreport>	
	and RESULT={<number>,<note>,<type>,<month>,<address>};
recordtype: set of Record*Id +> Recordtype
recordtype(dB,i)==
	if is_Article(get(dB,i)) 		then <article> 
	elseif is_Book(get(dB,i))	 	then <book> 
	elseif is_Inproceeding(get(dB,i)) 	then <inproceeding> 
	elseif is_Manual(get(dB,i))	 	then <manual> 
	else <techreport>
pre	i in set usedIds(dB)
post 	is_Article(get(dB,i)) 		and RESULT=<article> or
	is_Book(get(dB,i))	 	and RESULT=<book> or
	is_Inproceeding(get(dB,i)) 	and RESULT=<inproceeding> or
	is_Manual(get(dB,i))	 	and RESULT=<manual> or
	is_Techreport(get(dB,i))  	and RESULT=<techreport>;
required: Recordtype +> set of Valuetype
required(rt)==
	cases rt:
		<article> 	-> {<title>,<author>,<journal>,<year>},
		<book>		-> {<title>,<author>,<publisher>,<year>},
		<inproceeding>	-> {<title>,<author>,<booktitle>,<year>},
		<manual>	-> {<title>},
		<techreport>	-> {<title>,<author>,<institution>,<year>}
	end
post 	rt = <article>		
	and RESULT={<title>,<author>,<journal>,<year>} or
	rt = <book>		
	and RESULT={<title>,<author>,<publisher>,<year>} or
 	rt = <inproceeding>	
	and RESULT={<title>,<author>,<booktitle>,<year>} or
	rt = <manual>		
	and RESULT={<title>} or
	rt = <techreport>	
	and RESULT={<title>,<author>,<institution>,<year>};
substring(s:String,i:nat1,j:nat1) r:String
pre	i<j and j<=len(s)
post	exists s1,s2:String & len(s1)=i-1 and len(s2)=len(s)-j 
	and s=s1^r^s2;
usedIds: set of Record +> set of Id
usedIds(dB)==
	idset(dB,{})
post 	forall x in set dB & x.id in set RESULT and
	forall i in set RESULT & exists x in set dB & x.id = i;
idset: set of Record*set of Id +> set of Id
idset(dB,ids)==
  if dB={} 
  then ids
  else let record in set dB 
       in 
	     idset(dB\{record},ids union {record.id})
measure card dB;
  
operations
CREATE: Recordtype ==> Id
CREATE(e)==
(dcl i:nat1:=1;
(while
i in set usedIds(dB) do i:=i+1);
	cases e:
		<article> 	-> dB:=dB union 
					{mk_Article(i,<nil>,<nil>,<nil>,<nil>,
					<nil>,<nil>,<nil>,<nil>,<nil>)},
		<book>  	-> dB:=dB union 
					{mk_Book(i,<nil>,<nil>,<nil>,<nil>,
					<nil>,<nil>,<nil>,<nil>,<nil>,<nil>,
					<nil>)}, 
		<inproceeding>	-> dB:=dB union 
					{mk_Inproceeding(i,<nil>,<nil>,<nil>,
					<nil>,<nil>,<nil>,<nil>,<nil>,<nil>,
					<nil>,<nil>)}, 
		<manual>	-> dB:=dB union 
					{mk_Manual(i,<nil>,<nil>,<nil>,<nil>,
					<nil>,<nil>,<nil>,<nil>)},
		<techreport>	-> dB:=dB union 
					{mk_Techreport(i,<nil>,<nil>,<nil>,
					<nil>,<nil>,<nil>,<nil>,<nil>,<nil>)}
	end;
return i)
post 	RESULT not in set usedIds(dB~) and 
	e=<article> and 
	dB=dB~ union {mk_Article(RESULT,<nil>,<nil>,<nil>,<nil>,<nil>,<nil>,
				<nil>,<nil>,<nil>)}  
	or e=<book> and 
	dB=dB~ union {mk_Book(RESULT,<nil>,<nil>,<nil>,<nil>,<nil>,<nil>,
				<nil>,<nil>,<nil>,<nil>,<nil>)} 
	or e=<inproceeding> and 
	dB=dB~ union {mk_Inproceeding(RESULT,<nil>,<nil>,<nil>,<nil>,<nil>,
				<nil>,<nil>,<nil>,<nil>,<nil>,<nil>)} 
	or e=<manual> and
	dB=dB~ union {mk_Manual(RESULT,<nil>,<nil>,<nil>,<nil>,<nil>,<nil>,
				<nil>,<nil>)}  
	or e=<techreport>  and 
	dB=dB~ union {mk_Techreport(RESULT,<nil>,<nil>,<nil>,<nil>,<nil>,
				<nil>,<nil>,<nil>,<nil>)};
UPDATE(i:Id,f:Valuetype,v:Value)==
(if i in set usedIds(dB) and f in set field(recordtype(dB,i)) and 
isvalueoffield(v,f)
then (cases f:
	<address>	-> let urecord={mu(get(dB,i),address|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<author>	-> let urecord={mu(get(dB,i),author|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<booktitle>	-> let urecord={mu(get(dB,i),booktitle|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<edition>	-> let urecord={mu(get(dB,i),edition|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<editor>	-> let urecord={mu(get(dB,i),editor|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<institution>	-> let urecord=
				{mu(get(dB,i),institution|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<journal>	-> let urecord={mu(get(dB,i),journal|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<month>		-> let urecord={mu(get(dB,i),month|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<note>		-> let urecord={mu(get(dB,i),note|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<number>	-> let urecord={mu(get(dB,i),number|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<organization>	-> let urecord=
				{mu(get(dB,i),organization|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<pages>		-> let urecord={mu(get(dB,i),pages|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<publisher>	-> let urecord={mu(get(dB,i),publisher|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<title>		-> let urecord={mu(get(dB,i),title|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<type>		-> let urecord={mu(get(dB,i),type|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<volume>	-> let urecord={mu(get(dB,i),volume|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord,
	<year>		-> let urecord={mu(get(dB,i),year|->v)} in  
				dB:=(dB\{get(dB,i)}) union urecord
end;
if iscomplete(dB,i) and isidentical(dB) 
then (DELETE(i); error;)))
ext wr 	dB:set of Record
pre 	i in set usedIds(dB) and
	f in set field(recordtype(dB,i)) and
	isvalueoffield(v,f) and
	not (iscomplete(dB,i) and isidentical(dB))
post	getvalue(f,dB,i)=v and 
	dB\{get(dB,i)}=dB~\{get(dB~,i)} and
	forall x in set field(recordtype(dB,i))\{f} & 
	   getvalue(x,dB,i)=getvalue(x,dB~,i);
COMPLETE: Id ==> bool
COMPLETE(i)==
	return iscomplete(dB,i)
pre 	i in set usedIds(dB)
post	iscomplete(dB,i) <=> RESULT;
DELETE(i:Id)==
	if i in set usedIds(dB)
	then dB:=dB\{get(dB,i)}
ext wr 	dB:set of Record
pre 	i in set usedIds(dB)
post	dB~=dB union {get(dB~,i)};
SEARCH: String ==> set of Id
SEARCH(a)==
 (dcl ids:set of Id:={};
  for all record in set dB do
    if issubstring(a,record.author) 
    then ids:=ids union {record.id}
    else ids:=ids;
	return ids)
post forall i in set RESULT & issubstring(a,get(dB,i).author)
	and not exists record in set dB & 
	(record.id not in set RESULT and 
     issubstring(a,get(dB,i).author));
GET: Id ==> Record
GET(i)==
	return get(dB,i)
pre 	i in set usedIds(dB)
post	RESULT=get(dB,i);