/*
* ConcreteSplitViewer program for analazing splits.
* Copyright (C) 2006-2007 Mytinski Leonid (Leonid.Mytinski@gmail.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/*
* Tools.java
*
* Created on 2 Июнь 2006 г., 23:12
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package ru.concretesoft.concretesplitviewer;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
/**
*
* @author Mytinski Leonid
*
* Class content different method for analazing splits
* Класс содержит различные методы для анализа сплитов
*/
public class Tools {
private static final int TOTAL_DIST = 10000;
private Tools() { // (Pattern ?"Singleton"?) Запрещение создания экземпляра класса
}
/** Method for finding splits of "ideal" athlete, that showed detrmin time (first, second, third, ...)
* Метод возвращает сплиты виртуального спортсмена.
*
* @param all <code>Collection</code> of <code>Group</code>, from that will be finding "ideal" athlete (набор груп для вычисления виртуального спортсмена)
* @param any what time will be show on each lap (место занимаемое на перегоне)
*
* @return array of <code>Time</code> determin splits of "ideal" athlete or <code>null</code> if <code>Collection</code> presents groups for different distances
*/
public static Time[] getAnyBest(Collection<Group> all,int any){
Time[] best,secondBest;
int number;
secondBest = null;
if(all.size()>0) {
Iterator<Group> it = all.iterator();
TreeSet<Time>[] tS = null; // Content - all times of all athlets, who have to be in finding, on each lap
Distance d = null;
// Filling of tS
while(it.hasNext()){
Group g = it.next();
if(d == null)
d = g.getDistance();
else if(!d.equals(g.getDistance()))
return null;
else;
if(tS == null){ // if Set have not begined filling yet
number = d.getNumberOfCP();
tS = new TreeSet[number];
secondBest = new Time[number];
}else;
for(int i=0;i<tS.length;i++){
HashSet<Time> hS = g.getTimesOnLap(i+1);
if(tS[i]==null) tS[i] = new TreeSet<Time>();
tS[i].addAll(hS);
}
}
// If on first lap one or more times then do finding else set return value to null
if(tS[0].size()>=1){
for(int i=0;i<tS.length;i++){
if(tS[i].size()>=any){
if((tS[i].first().getTimeInSeconds()!=0)||(tS[i].size()==any))
secondBest[i] = (Time)tS[i].toArray()[any-1];
else if(tS[i].size()>any)
secondBest[i] = (Time)tS[i].toArray()[any];
else
secondBest[i] = (Time)tS[i].toArray()[tS[i].size()-1];
}
else if(tS[0].size()>=1){
secondBest[i] = tS[i].last();
}
else
secondBest[i] = new Time(0,2);
}
} else secondBest = null;
}
return secondBest;
}
/** Method for calculating mean speed on lap from <code>n-1</code> to <code>n</code>
*
* @param d <code>Distance</code> on that was run athlete
* @param a <code>Athlete</code> whose speed will be calculating
* @param n control point's number
*
* @return <code>Time</code> that determin mean speed in format time/km
*/
public static Time getMeanSpeedOnLap(Distance d, Athlete a, int n){
int lengOfDist = d.getLengthOfDist(n);
double lengKM = lengOfDist/1000.0;
Time time = a.getLap(n);
int timeS = time.getTimeInSeconds();
int speedS = (int)(timeS/lengKM);
Time speed = new Time(0,2);
speed.setTimeInSeconds(speedS);
return speed;
}
/** Method for calculating "mean" speed from set of laps with equlas weights
* meanSpeed = mean ariphmetic of mean speed on each lap
*
* @param d <code>Distance</code> on that was run athlete
* @param a <code>Athlete</code> whose speed will be calculating
* @param laps array of lap's numbers from that would be calculating "mean" speed. If equals <code>null</code> then used all laps of distance
*
*
* @return <code>Time</code> that determin "mean" speed in format time/km
*/
public static Time getMeanSpeed(Distance d, Athlete a, int[] laps){
boolean allLaps = (laps == null);
int numberOfLaps = allLaps ? d.getNumberOfCP() : laps.length;
Time speed = new Time(0,2);
for (int i = 0; i < numberOfLaps ; i++){
speed.addTime(getMeanSpeedOnLap(d, a, (allLaps ? (i+1) : laps[i]) ));
}
speed.setTimeInSeconds(speed.getTimeInSeconds()/numberOfLaps);
return speed;
}
/** Method for calculating "mean" speed on all laps of distance
*
* @param d <code>Distance</code> on that was run athlete
* @param a <code>Athlete</code> whose speed will be calculating
*
* @return <code>Time</code> that determin "mean" speed in format time/km
*
* @see #getMeanSpeed(Distance, Athlete, int[])
*/
public static Time getMeanSpeed(Distance d, Athlete a){
return getMeanSpeed(d, a, null);
}
/** Method for calculating "mean" speed more complex way than <code>getMeanSpeed</code>
* First calculating "mean" speed with help <code>getMeanSpeed</code>.
* Then remove from array of laps element with maximum difference with mean value.
* And call yourself with new array
*
* @param d <code>Distance</code> on that was run athlete
* @param a <code>Athlete</code> whose speed will be calculating
* @param laps array of lap's numbers from that would be calculating "mean" speed. If equals <code>null</code> then used all laps of distance
* @param k parameter that determin relative maximum of difference laps with mean ( value from <code>0</code> to <code>1</code> )
*
* @return <code>Time</code> that determin "mean" speed in format time/km
*/
public static Time getComplexMeanSpeed(Distance d, Athlete a, int[] laps, double k){
Time speedSimple = getMeanSpeed(d, a, laps);
int j=-1;
int max=0;
boolean allLaps = (laps == null);
int numberOfLaps = allLaps ? d.getNumberOfCP() : laps.length;
for(int i=0; i < numberOfLaps; i++){
int speedS = speedSimple.getTimeInSeconds();
int speed = getMeanSpeedOnLap(d, a, (allLaps ? i+1: laps[i]) ).getTimeInSeconds();
// System.out.println(speed+" "+speedS);
if(Math.abs(speed-speedS)/(double)speedS >= k){
if(max < Math.abs(speed-speedS)){
max = Math.abs(speed-speedS);
j=i;
}else;
}else;
}
if(j<0) return speedSimple;
else{
int[] lapsN = new int[numberOfLaps-1];
for(int i=0; i < numberOfLaps; i++){
if(i<j){
lapsN[i] = allLaps ? i+1 : laps[i];
}
else if(i>j){
lapsN[i-1] = allLaps ? i+1 : laps[i];
}
else;
}
return getComplexMeanSpeed(d,a,lapsN,k);
}
}
/** Method for calculating "mean" speed from all laps of distance
*
* @param d <code>Distance</code> on that was run athlete
* @param a <code>Athlete</code> whose speed will be calculating
* @param k parameter that determin relative maximum of difference laps with mean ( value from <code>0</code> to <code>1</code> )
*
* @return <code>Time</code> that determin "mean" speed in format time/km
*
* @see #getComplexMeanSpeed(Distance, Athlete, int[], double)
*/
public static Time getComplexMeanSpeed(Distance d, Athlete a, double k){
return getComplexMeanSpeed(d, a, null, k);
}
/** Method for calculating "mean" speed more complex way than <code>getMeanSpeed</code>
* First calculating "mean" speed with help <code>getMeanSpeed</code>.
* Then remove from array of laps elementes with difference beetwen mean value more than k*(mean value).
* And return result of call <code>getMeanSpeed</code> with new array.
*
* @param d <code>Distance</code> on that was run athlete
* @param a <code>Athlete</code> whose speed will be calculating
* @param laps array of lap's numbers from that would be calculating "mean" speed
* @param k parameter that determin relative maximum of difference laps with mean ( value from <code>0</code> to <code>1</code> )
*
* @return <code>Time</code> that determin "mean" speed in format time/km
*/
public static Time getComplexMeanSpeed_First(Distance d, Athlete a, int[] laps, double k){
Time speedSimple = getMeanSpeed(d, a, laps);
int j=0;
for(int i=0; i < laps.length; i++){
int speedS = speedSimple.getTimeInSeconds();
int speed = getMeanSpeedOnLap(d, a, laps[i]).getTimeInSeconds();
if(Math.abs(speed-speedS)/(double)speedS < k){
j++;
// System.out.println(speed+" "+speedS);
}
else;
}
int[] lapsN = new int[j];
j=0;
for(int i=0; i < laps.length; i++){
int speedS = speedSimple.getTimeInSeconds();
int speed = getMeanSpeedOnLap(d, a, laps[i]).getTimeInSeconds();
if(Math.abs(speed-speedS)/(double)speedS < k){
lapsN[j] = laps[i];
j++;
}
else;
}
return getMeanSpeed(d, a, lapsN);
}
/** Method calculating (approximately) length of each lap from full length of distance and second best time on each lap
*
* @param grV Set of group from that will be calculating second best time. Groups in set should be with one distance.
*
* @return array of length of each lap in meters or <code>null</code> if <code>grV</code> presents groups for different distances
*/
public static int[] calculatLengthsOfLaps(List<Group> grV){
Time[] secBest = getAnyBest(grV,2);
if(secBest!=null){
Distance d = grV.get(0).getDistance();
int totTime=0;
int[] val = new int[secBest.length];
for(int i=0;i<secBest.length;i++){
totTime+=secBest[i].getTimeInSeconds();
}
for(int i=0;i<secBest.length;i++){
val[i] = (int)(((double)secBest[i].getTimeInSeconds() / totTime)*TOTAL_DIST);
}
return val;
}
else
return null;
}
/** Calculate total length of distance consist of given laps
*
* @param d distance for calculating
* @param laps set of laps for calculating
*
* @return length of distance consist of given laps or <code>-1<code> if length of array <code>laps</code> more than number of control points in distance
*/
public static int calculateTotalLength(Distance d, int[] laps){
if(laps==null){
return TOTAL_DIST;
}else if(laps.length > d.getNumberOfCP()){
return -1;
}else{
int totL = 0;
for(int i=0;i<laps.length; i++){
totL += d.getLengthOfDist(laps[i]);
}
return totL;
}
}
public static int calculateTotalLength(Distance d){
return calculateTotalLength(d, null);
}
}