From 1cfb48a9a65f5f57fec5693a1c723f0ec60f5fe1 Mon Sep 17 00:00:00 2001 From: Jed Barber Date: Sun, 5 Feb 2017 19:13:41 +1100 Subject: Candidates package done --- src/candidates.adb | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++--- src/candidates.ads | 136 +++++++++++++++++++++------ src/stv.adb | 1 + 3 files changed, 368 insertions(+), 40 deletions(-) diff --git a/src/candidates.adb b/src/candidates.adb index 3701ffc..bb09a40 100644 --- a/src/candidates.adb +++ b/src/candidates.adb @@ -1,42 +1,291 @@ with Ada.Text_IO; -use Ada.Text_IO; with CSV; package body Candidates is + function To_String + (Input_Candidate : in Candidate; + Delimiter : in Character := ',') + return String + is + package My_CSV is new CSV (Delimiter => Delimiter); + use type My_CSV.String_Vectors.Vector; + + My_Record : My_CSV.CSV_Record; + begin + My_Record := + My_CSV.String_Vectors.Empty_Vector & + Input_Candidate.Group & + Input_Candidate.Group_Rank & + Input_Candidate.First_Name & + Input_Candidate.Last_Name & + Input_Candidate.Party; + return My_CSV.Unparse_Record (My_Record); + end To_String; + + + + procedure Read_Candidates - (Data_File, State : in String; - Above_Ballot : out Above_Line_Ballot; - Below_Ballot : out Below_Line_Ballot; - Candidate_List : out Candidate_Vectors.Vector) + (Filename, State : in String; + Candidate_List : out Candidate_Vector) is + package My_CSV is new CSV; + use Ada.Text_IO; + use type Ada.Containers.Count_Type; + use type SU.Unbounded_String; + + Input_File : File_Type; + Current_Record : My_CSV.CSV_Record; + Current_Candidate : Candidate; + begin + Open (Input_File, In_File, Filename); + Candidate_List := (Vec => Candidate_Vectors.Empty_Vector); + + while not End_Of_File (Input_File) loop + Current_Record := My_CSV.Parse_Line (Get_Line (Input_File)); + + -- all the field numbers here correspond to how + -- AEC Senate candidate data is arranged in csv format + if Current_Record.Length = 25 and then + Current_Record.Element (2) = "S" and then + Current_Record.Element (3) = State + then + Current_Candidate := + (First_Name => Current_Record.Element (8), + Last_Name => Current_Record.Element (7), + Group => Current_Record.Element (5), + Group_Rank => Current_Record.Element (6), + Party => Current_Record.Element (9)); + Candidate_List.Vec.Append (Current_Candidate); + end if; + end loop; + + Close (Input_File); + end Read_Candidates; + + + + + function First + (Candidate_List : in Candidate_Vector) + return CandidateID is + begin + return Candidate_List.Vec.First_Index; + end First; + + + + + function Last + (Candidate_List : in Candidate_Vector) + return CandidateID is + begin + return Candidate_List.Vec.Last_Index; + end Last; + + + + + function Lookup + (Candidate_List : in Candidate_Vector; + Index : in CandidateID) + return Candidate is begin - end Read_Candidate; + return Candidate_List.Vec.Element (Index); + end Lookup; + + + + + function First + (CandidateID_List : in CandidateID_Vector) + return Positive is + begin + return CandidateID_List.Vec.First_Index; + end First; + + + + + function Last + (CandidateID_List : in CandidateID_Vector) + return Positive is + begin + return CandidateID_List.Vec.Last_Index; + end Last; + + + + + function Lookup + (CandidateID_List : in CandidateID_Vector; + Index : in Positive) + return CandidateID is + begin + return CandidateID_List.Vec.Element (Index); + end Lookup; + + + + + function "<" + (Left, Right : Candidate) + return Boolean + is + use type SU.Unbounded_String; + begin + if SU.Length (Left.Group) = SU.Length (Right.Group) then + if Left.Group = Right.Group then + return Left.Group_Rank < Right.Group_Rank; + else + return Left.Group < Right.Group; + end if; + else + return SU.Length (Left.Group) < SU.Length (Right.Group); + end if; + end "<"; + + + + + function Generate_Above + (Candidate_List : in Candidate_Vector) + return Above_Line_Ballot + is + use type Ada.Containers.Count_Type; + use type SU.Unbounded_String; + + Result : Above_Line_Ballot; + Working_Vector : CandidateID_Vector; + Current_Group : SU.Unbounded_String; + Current_Index : CandidateID; + begin + Result := (Vec => Above_Line_Ballots.Empty_Vector); + + if Candidate_List.Vec.Length = 0 then + return Result; + end if; + + Current_Index := Candidate_List.Vec.First_Index; + while Current_Index <= Candidate_List.Vec.Last_Index loop + Current_Group := Candidate_List.Vec.Element (Current_Index).Group; + + -- the assumption is that the "UG" group is always last + -- a fairly safe assumption given alphabetical group order + exit when Current_Group = "UG"; + + Working_Vector := (Vec => CandidateID_Vectors.Empty_Vector); + loop + Working_Vector.Vec.Append (Current_Index); + Current_Index := Current_Index + 1; + exit when Current_Index > Candidate_List.Vec.Last_Index or else + Current_Group /= Candidate_List.Vec.Element (Current_Index).Group; + end loop; + Result.Vec.Append (Working_Vector); + end loop; + + return Result; + end Generate_Above; + + + + + function Generate_Below + (Candidate_List : in Candidate_Vector) + return Below_Line_Ballot + is + Result : Below_Line_Ballot; + begin + Result := (Vec => CandidateID_Vectors.Empty_Vector); + for ID in CandidateID range Candidate_List.Vec.First_Index .. Candidate_List.Vec.Last_Index loop + Result.Vec.Append (ID); + end loop; + return Result; + end Generate_Below; + + + + + procedure Generate_Ballots + (Candidate_List : in Candidate_Vector; + Above_Ballot : out Above_Line_Ballot; + Below_Ballot : out Below_Line_Ballot) + is + package Sorting is new Candidate_Vectors.Generic_Sorting; + + My_Candidate_List : Candidate_Vector; + begin + My_Candidate_List := Candidate_List; + Sorting.Sort (My_Candidate_List.Vec); + Above_Ballot := Generate_Above (My_Candidate_List); + Below_Ballot := Generate_Below (My_Candidate_List); + end Generate_Ballots; + + + + + function First + (Above_Ballot : in Above_Line_Ballot) + return Positive is + begin + return Above_Ballot.Vec.First_Index; + end First; + + + + + function Last + (Above_Ballot : in Above_Line_Ballot) + return Positive is + begin + return Above_Ballot.Vec.Last_Index; + end Last; function Lookup (Above_Ballot : in Above_Line_Ballot; - Index : in Natural) - return CandidateID_Vectors.Vector is + Index : in Positive) + return CandidateID_Vector is begin - return Above_Ballot.Element (Index); + return Above_Ballot.Vec.Element (Index); end Lookup; + function First + (Below_Ballot : in Below_Line_Ballot) + return Positive is + begin + return Below_Ballot.Vec.First_Index; + end First; + + + + + function Last + (Below_Ballot : in Below_Line_Ballot) + return Positive is + begin + return Below_Ballot.Vec.Last_Index; + end Last; + + + + function Lookup (Below_Ballot : in Below_Line_Ballot; - Index : in Natural) + Index : in Positive) return CandidateID is begin - return Below_Ballot.Element (Index); + return Below_Ballot.Vec.Element (Index); end Lookup; diff --git a/src/candidates.ads b/src/candidates.ads index 90b2425..fb04a4e 100644 --- a/src/candidates.ads +++ b/src/candidates.ads @@ -1,73 +1,151 @@ -with Ada.Containers.Vectors; +private with Ada.Strings.Unbounded; +private with Ada.Containers.Vectors; package Candidates is type Candidate is private; - type CandidateID is Natural; + type CandidateID is new Positive; + + + function To_String + (Input_Candidate : in Candidate; + Delimiter : in Character := ',') + return String; + + + + + type Candidate_Vector is private; + type CandidateID_Vector is private; + + + procedure Read_Candidates + (Filename, State : in String; + Candidate_List : out Candidate_Vector); + + + function First + (Candidate_List : in Candidate_Vector) + return CandidateID; + + + function Last + (Candidate_List : in Candidate_Vector) + return CandidateID; + + + function Lookup + (Candidate_List : in Candidate_Vector; + Index : in CandidateID) + return Candidate; + + + function First + (CandidateID_List : in CandidateID_Vector) + return Positive; + + + function Last + (CandidateID_List : in CandidateID_Vector) + return Positive; + + + function Lookup + (CandidateID_List : in CandidateID_Vector; + Index : in Positive) + return CandidateID; + + type Above_Line_Ballot is private; type Below_Line_Ballot is private; - package Candidate_Vectors is new Ada.Containers.Vectors - (Index_Type => CandidateID, - Element_Type => Candidate); + procedure Generate_Ballots + (Candidate_List : in Candidate_Vector; + Above_Ballot : out Above_Line_Ballot; + Below_Ballot : out Below_Line_Ballot); - package CandidateID_Vectors is new Ada.Containers.Vectors - (Index_Type => Natural, - Element_Type => CandidateID); + function First + (Above_Ballot : in Above_Line_Ballot) + return Positive; - procedure Read_Candidates - (Data_File, State : in String; - Above_Ballot : out Above_Line_Ballot; - Below_Ballot : out Below_Line_Ballot; - Candidate_List : out Candidate_Vectors.Vector); + function Last + (Above_Ballot : in Above_Line_Ballot) + return Positive; function Lookup (Above_Ballot : in Above_Line_Ballot; - Index : in Natural) - return CandidateID_Vectors.Vector; + Index : in Positive) + return CandidateID_Vector; + + + function First + (Below_Ballot : in Below_Line_Ballot) + return Positive; + + + function Last + (Below_Ballot : in Below_Line_Ballot) + return Positive; function Lookup (Below_Ballot : in Below_Line_Ballot; - Index : in Natural) + Index : in Positive) return CandidateID; private + package SU renames Ada.Strings.Unbounded; + + type Candidate is record - ID : CandidateID; - First_Name : String; - Last_Name : String; - Group : String; - Group_Rank : Natural; - Party : String; + First_Name : SU.Unbounded_String; + Last_Name : SU.Unbounded_String; + Group : SU.Unbounded_String; + Group_Rank : SU.Unbounded_String; + Party : SU.Unbounded_String; end record; - package Above_Line_Ballots is new Ada.Containers.Vectors - (Index_Type => Natural, - Element_Type => CandidateID_Vectors.Vector); + package Candidate_Vectors is new Ada.Containers.Vectors + (Index_Type => CandidateID, + Element_Type => Candidate); + type Candidate_Vector is record + Vec : Candidate_Vectors.Vector; + end record; - package Below_Line_Ballots is new Ada.Containers.Vectors - (Index_Type => Natural, + package CandidateID_Vectors is new Ada.Containers.Vectors + (Index_Type => Positive, Element_Type => CandidateID); + type CandidateID_Vector is record + Vec : CandidateID_Vectors.Vector; + end record; + + + package Above_Line_Ballots is new Ada.Containers.Vectors + (Index_Type => Positive, + Element_Type => CandidateID_Vector); + type Above_Line_Ballot is record + Vec : Above_Line_Ballots.Vector; + end record; - type Above_Line_Ballot is Above_Line_Ballots.Vector; - type Below_Line_Ballot is Below_Line_Ballots.Vector; + type Below_Line_Ballot is record + Vec : CandidateID_Vectors.Vector; + end record; end Candidates; diff --git a/src/stv.adb b/src/stv.adb index 3297da2..a7d6c6d 100644 --- a/src/stv.adb +++ b/src/stv.adb @@ -3,6 +3,7 @@ with Ada.Text_IO; with Ada.Strings.Unbounded; with CSV; +with Candidates; procedure STV is -- cgit