/** @file NumStationSet.java
*
* @author marco corvi
* @date jan 2013
*
* @brief TopoDroid survey stations container (RB-tree)
* --------------------------------------------------------
* Copyright This sowftare is distributed under GPL-3.0 or later
* See the file COPYING.
* --------------------------------------------------------
*/
package com.topodroid.DistoX;
import java.util.Stack;
import java.util.List;
import java.util.ArrayList;
import android.util.Log;
class NumStationSet
{
// -------------------------------------------------------------
static final boolean BLACK = true;
static final boolean RED = false;
static int compare( String s1, String s2 )
{
int l1 = s1.length();
int l2 = s2.length();
int kk = ( l1 < l2 )? l1 : l2;
for ( int k=0; k < kk; ++k ) {
if ( s1.charAt(k) < s2.charAt(k) ) return -1;
if ( s1.charAt(k) > s2.charAt(k) ) return +1;
}
if ( l1 < l2 ) return -1;
if ( l1 > l2 ) return +1;
return 0;
}
private class NumStationNode
{
NumStationNode parent;
NumStationNode left;
NumStationNode right;
boolean color;
NumStation value;
NumStationNode( NumStation v )
{
parent = null;
left = null;
right = null;
color = RED;
value = v;
}
// void dump( int indent )
// {
// for ( int k = indent; k > 0; --k ) {
// System.out.print( " ");
// }
// System.out.println( toString() );
// if ( left != null ) { System.out.print("L: "); left.dump( indent+1 ); }
// if ( right != null ) { System.out.print("R: "); right.dump( indent+1 ); }
// }
// public String toString() { return value + ( color ? "b" : "r"); }
// boolean checkRB()
// {
// if ( ! color ) {
// return ( left == null ) || ( left.color && left.checkRB() ) &&
// ( right == null ) || ( right.color && right.checkRB() );
// } else {
// return ( left == null ) || ( left.checkRB() ) &&
// ( right == null ) || ( right.checkRB() );
// }
// }
// int pathLength()
// {
// int left_length = ( left == null )? 0 : ( left.pathLength() + ( left.color? 1 : 0 ));
// int right_length = ( right == null )? 0 : ( right.pathLength() + ( right.color? 1 : 0 ));
// return ( left_length > right_length )? left_length : right_length;
// }
// boolean checkPath()
// {
// int left_length = ( left == null )? 0 : ( left.pathLength() + ( left.color? 1 : 0 ));
// int right_length = ( right == null )? 0 : ( right.pathLength() + ( right.color? 1 : 0 ));
// if ( left_length != right_length ) return false;
// return ( left == null || left.checkPath() ) && ( right == null || right.checkPath() );
// }
NumStation get( String name )
{
int c = compare( value.name, name );
// Log.v("DistoX", value.name + " get " + name + " " + c + " left " + ((left==null)? "null" : left.value.name) +
// " right " + ((right==null)? "null" : right.value.name ) );
if ( c == 0 ) return value;
if ( c < 0 ) return ( left == null )? null : left.get( name );
return ( right == null )? null : right.get( name );
}
void setShortPathDist( float p )
{
value.mShortpathDist = p;
if ( left != null ) left.setShortPathDist( p );
if ( right != null ) right.setShortPathDist( p );
}
void setCoords( boolean b )
{
value.mHasCoords = b;
if ( left != null ) left.setCoords( b );
if ( right != null ) right.setCoords( b );
}
void setAzimuths()
{
value.setAzimuths();
if ( left != null ) left.setAzimuths();
if ( right != null ) right.setAzimuths();
}
void updateHidden( NumStation st, int dh, Stack<NumStation> stack )
{
if ( value.mParent == st ) {
value.mHidden += dh;
stack.push( value );
}
if ( left != null ) left.updateHidden( st, dh, stack );
if ( right != null ) right.updateHidden( st, dh, stack );
}
}
ArrayList< NumStation > mStations;
NumStationNode mRoot;
NumStationSet()
{
mRoot = null;
mStations = new ArrayList< NumStation >();
}
void setShortestPath( float p )
{
if ( mRoot == null ) return;
mRoot.setShortPathDist( p );
}
void setCoords( boolean b )
{
if ( mRoot == null ) return;
mRoot.setCoords( b );
}
void setAzimuths( )
{
if ( mRoot == null ) return;
mRoot.setAzimuths();
}
void updateHidden( NumStation st, int dh, Stack<NumStation> stack )
{
if ( mRoot == null ) return;
mRoot.updateHidden( st, dh, stack );
}
int size() { return mStations.size(); }
List< NumStation > getStations() { return mStations; }
boolean addStation( NumStation v )
{
// Log.v("DistoX", "add station " + v.name + " root " + ((mRoot != null)? mRoot.value.name : "null") );
boolean ret = true;
NumStationNode n = new NumStationNode( v );
if ( mRoot == null ) {
mRoot = n;
mRoot.color = BLACK;
} else {
for ( NumStationNode n0 = mRoot; ; ) {
int c = compare( n0.value.name, v.name );
if ( c < 0 ) {
if ( n0.left == null ) {
n0.left = n;
n.parent = n0;
break;
} else {
n0 = n0.left;
}
} else if ( c > 0 ) {
if ( n0.right == null ) {
n0.right = n;
n.parent = n0;
break;
} else {
n0 = n0.right;
}
} else {
// Log.v("DistoX", "Double insertion of station " + v.name );
ret = false;
break;
}
}
// rebalance
if ( ret ) insert_case1( n );
}
if ( ret ) mStations.add( v );
// Log.v("DistoX", "added station " + v.name + " root " + mRoot.value.name );
return ret;
}
NumStation getStation( String name )
{
// Log.v("DistoX", "stations set size " + size() );
return ( mRoot == null )? null : mRoot.get( name );
}
// -----------------------------------------------------
private void insert_case1( NumStationNode n )
{
if ( n.parent == null ) {
n.color = BLACK;
} else {
insert_case2( n );
}
}
// n.parent != null
private void insert_case2( NumStationNode n )
{
if ( n.parent.color ) return; // isBlack( n.parent )
insert_case3( n );
}
// n.parent != null && n.parent RED
private void insert_case3( NumStationNode n )
{
NumStationNode u = uncle( n );
if ( u != null && isRed( u ) ) {
n.parent.color = BLACK;
u.color = BLACK;
NumStationNode g = grandparent( n );
g.color = RED;
insert_case1( g );
} else {
insert_case4( n );
}
}
// n.parent == g.left && n = n.parent.right ( n.parent RED )
// or symmetric
private void insert_case4( NumStationNode n )
{
NumStationNode p = n.parent;
NumStationNode g = p.parent;
if ( isRight( n ) && isLeft( p ) ) { // rotate_left( n, p, g );
n.parent = g;
if ( g != null ) {
g.left = n;
} else {
mRoot = n;
}
if ( n.left != null ) n.left.parent = p;
p.right = n.left;
p.parent = n;
n.left = p;
n = n.left; // continue with n.left
} else if ( isLeft( n ) && isRight( p ) ) { // rotate_right( n, p, g );
n.parent = g;
if ( g != null ) {
g.right = n;
} else {
mRoot = n;
}
if ( n.right != null ) n.right.parent = p;
p.left = n.right;
p.parent = n;
n.right = p;
n = n.right; // continue with n.right
}
insert_case5( n );
}
// n.parent RED but n.uncle BLACK
private void insert_case5( NumStationNode n )
{
NumStationNode g = grandparent( n );
NumStationNode p = n.parent;
p.color = BLACK;
g.color = RED;
NumStationNode gp = g.parent;
if ( gp != null ) {
if ( g == gp.left ) {
gp.left = p;
} else {
gp.right = p;
}
} else {
mRoot = p;
}
p.parent = gp;
if ( isLeft( n ) ) { // rotate_right( n.parent, g, g.parent );
// assert( p == g.left );
g.left = p.right;
if ( p.right != null ) p.right.parent = g;
p.right = g;
g.parent = p;
} else { // rotate_left( n.parent, g, g.parent );
// assert( p == g.right );
g.right = p.left;
if ( p.left != null ) p.left.parent = g;
p.left = g;
g.parent = p;
}
}
private NumStationNode grandparent( NumStationNode n )
{
return ( n != null && n.parent != null )? n.parent.parent : null;
}
private NumStationNode uncle( NumStationNode n )
{
NumStationNode p = n.parent;
if ( p == null ) return null;
NumStationNode g = p.parent;
if ( g == null ) return null;
return ( p == g.left )? g.right : g.left;
}
private boolean isBlack( NumStationNode n ) { return ( n == null ) || n.color; }
private boolean isRed( NumStationNode n ) { return ( n != null ) && (! n.color ); }
// prereq. n.parent != null
private boolean isLeft( NumStationNode n ) { return ( n == n.parent.left ); }
private boolean isRight( NumStationNode n ) { return ( n == n.parent.right ); }
// boolean check()
// {
// if ( isRed(mRoot) ) return false;
// if ( mRoot != null && ! mRoot.checkRB() ) return false;
// if ( mRoot != null && ! mRoot.checkPath() ) return false;
// return true;
// }
}