1
1
use anyhow:: anyhow;
2
2
use core:: str:: FromStr ;
3
- use petgraph:: graph:: { Graph , NodeIndex } ;
3
+ use petgraph:: graph:: { DiGraph , NodeIndex } ;
4
4
use serde:: Deserialize ;
5
5
use std:: fmt;
6
6
@@ -105,7 +105,7 @@ impl From<Course> for DatabaseNode {
105
105
/// Abstraction over some way to retrieve course info for simplicity.
106
106
/// There must only be one entry for each course.
107
107
pub struct CourseDatabase {
108
- courses : Graph < DatabaseNode , Relation > ,
108
+ pub courses : DiGraph < DatabaseNode , Relation > ,
109
109
}
110
110
111
111
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
@@ -135,11 +135,11 @@ impl CourseDatabase {
135
135
course_list. dedup_by ( |a, b| a. id == b. id ) ;
136
136
137
137
// Build the course graph
138
- let mut courses = Graph :: new ( ) ;
138
+ let mut courses = DiGraph :: new ( ) ;
139
139
let mut edge_queue = Vec :: < ( NodeIndex , CourseId , Relation ) > :: new ( ) ;
140
140
141
141
fn descend_deptree (
142
- courses : & mut Graph < DatabaseNode , Relation > ,
142
+ courses : & mut DiGraph < DatabaseNode , Relation > ,
143
143
edge_queue : & mut Vec < ( NodeIndex , CourseId , Relation ) > ,
144
144
node : & NodeIndex ,
145
145
requirement : & Requirement ,
@@ -158,6 +158,7 @@ impl CourseDatabase {
158
158
}
159
159
}
160
160
req => {
161
+ // Note that A corequisite B && B !corequisite A is true.
161
162
edge_queue. push ( ( * node, req. unwrap ( ) , req. try_into ( ) . unwrap ( ) ) ) ;
162
163
}
163
164
}
@@ -204,13 +205,13 @@ impl CourseDatabase {
204
205
Ok ( Self { courses } )
205
206
}
206
207
207
- pub fn get ( & self , id : & CourseId ) -> Option < Course > {
208
+ pub fn index_of ( & self , id : & CourseId ) -> Option < NodeIndex > {
208
209
let idx = self
209
210
. courses
210
211
. node_indices ( )
211
212
. find ( |node_idx| self . courses [ * node_idx] . has_id ( id) ) ?;
212
213
Some ( match & self . courses [ idx] {
213
- DatabaseNode :: Course ( course) => course . clone ( ) ,
214
+ DatabaseNode :: Course ( course) => idx ,
214
215
_ => unreachable ! ( ) ,
215
216
} )
216
217
}
@@ -219,6 +220,8 @@ impl CourseDatabase {
219
220
#[ cfg( test) ]
220
221
mod tests {
221
222
use super :: * ;
223
+ use petgraph:: visit:: EdgeRef ;
224
+
222
225
static CMPUT_SMALL : & ' static str = r#"[
223
226
(
224
227
id: (subject_id: "CMPUT", class_id: 101),
@@ -248,72 +251,76 @@ mod tests {
248
251
requirements: Some(Prereq((subject_id: "MATH", class_id: 111))),
249
252
),
250
253
]"# ;
254
+ #[ track_caller]
255
+ fn assert_in_db ( db : & CourseDatabase , id : & CourseId ) -> NodeIndex {
256
+ let Some ( course_idx) = db. index_of ( id) else {
257
+ panic ! ( "{} not in the Database" , id) ;
258
+ } ;
259
+
260
+ course_idx
261
+ }
251
262
252
263
#[ test]
253
264
fn cmput_small ( ) {
254
- let cd = match CourseDatabase :: new ( CMPUT_SMALL ) {
255
- Ok ( cd ) => cd ,
265
+ let db = match CourseDatabase :: new ( CMPUT_SMALL ) {
266
+ Ok ( db ) => db ,
256
267
Err ( err) => panic ! ( "Faild to build CourseDatabase {:?}" , err) ,
257
268
} ;
258
269
259
- assert ! (
260
- matches!(
261
- cd. get( & CourseId {
262
- subject_id: "CMPUT" . into( ) ,
263
- class_id: 101
264
- } ) ,
265
- Some ( _)
266
- ) ,
267
- "CMPUT 101 not in the Database"
270
+ let cmput_101_id = CourseId {
271
+ subject_id : "CMPUT" . into ( ) ,
272
+ class_id : 101 ,
273
+ } ;
274
+ let math_112_id = CourseId {
275
+ subject_id : "MATH" . into ( ) ,
276
+ class_id : 112 ,
277
+ } ;
278
+
279
+ let cmput_101 = assert_in_db ( & db, & cmput_101_id) ;
280
+ let cmput_102 = assert_in_db (
281
+ & db,
282
+ & CourseId {
283
+ subject_id : "CMPUT" . into ( ) ,
284
+ class_id : 102 ,
285
+ } ,
286
+ ) ;
287
+ let math_111 = assert_in_db (
288
+ & db,
289
+ & CourseId {
290
+ subject_id : "MATH" . into ( ) ,
291
+ class_id : 111 ,
292
+ } ,
293
+ ) ;
294
+ let math_112 = assert_in_db ( & db, & math_112_id) ;
295
+
296
+ assert_eq ! (
297
+ db. courses. edges( cmput_101) . count( ) ,
298
+ 0 ,
299
+ "CMPUT 101 was parsed as having dependencies when it has none"
268
300
) ;
301
+ let mut desired = vec ! [ cmput_101_id, math_112_id] ;
302
+
303
+ for edge in db. courses . edges ( cmput_102) {
304
+ assert_eq ! (
305
+ * edge. weight( ) ,
306
+ Relation :: Prereq ,
307
+ "All dependencies of CMPUT 102 should be prereqs"
308
+ ) ;
309
+ assert_eq ! (
310
+ edge. source( ) ,
311
+ cmput_102,
312
+ "There is something wrong with petgraph or the test"
313
+ ) ;
314
+ let Some ( ( idx, cid) ) = desired
315
+ . iter ( )
316
+ . enumerate ( )
317
+ . find ( |cid| db. courses [ edge. target ( ) ] . has_id ( cid. 1 ) )
318
+ else {
319
+ panic ! ( "There was an unexpected dependency on CMPUT 102" ) ;
320
+ } ;
321
+ desired. remove ( idx) ;
322
+ }
323
+
324
+ assert ! ( desired. is_empty( ) , "CMPUT 101 had dependencies missing" ) ;
269
325
}
270
326
}
271
-
272
- // impl Default for CourseDatabase {
273
- // // Simple course catalog to use for testing.
274
- // fn default() -> Self {
275
- // use Requirement::*;
276
- // let cmput_101 = Course::new("CMPUT", 101, None);
277
- // let cmput_102 = Course::new("CMPUT", 102,
278
- // Some(Prereq(cmput_101.id.clone())));
279
- //
280
- // let math_111 = Course::new("MATH", 111, None);
281
- // let math_112 = Course::new("MATH", 112,
282
- // Some(Prereq(math_111.id.clone())));
283
- //
284
- // let cmput_174 = Course::new(
285
- // "CMPUT",
286
- // 174,
287
- // Some(And(vec![
288
- // Prereq(math_112.id.clone()),
289
- // Coreq(cmput_102.id.clone()),
290
- // ])),
291
- // );
292
- // let cmput_175 = Course::new("CMPUT", 175,
293
- // Some(Prereq(cmput_174.id.clone())));
294
- //
295
- // let cmput_274 = Course::new("CMPUT", 274, None);
296
- // let cmput_275 = Course::new("CMPUT", 275,
297
- // Some(Prereq(cmput_274.id.clone())));
298
- //
299
- // let cmput_202 = Course::new("CMPUT", 202, None);
300
- //
301
- // let cmput_322 = Course::new(
302
- // "CMPUT",
303
- // 322,
304
- // Some(And(vec![
305
- // Or(vec![
306
- // Requirement::Prereq(cmput_275.id.clone()),
307
- // Requirement::Prereq(cmput_175.id.clone()),
308
- // ]),
309
- // Requirement::Prereq(cmput_202.id.clone()),
310
- // ])),
311
- // );
312
- // Self {
313
- // courses: vec![
314
- // cmput_101, cmput_102, math_111, math_112, cmput_174,
315
- // cmput_175, cmput_274, cmput_275, cmput_202, cmput_322,
316
- // ],
317
- // }
318
- // }
319
- // }
0 commit comments