@function ch-max($v1, $v2) {
  @return if($v1 > $v2, $v1, $v2);
}

@function ch-min($v1, $v2) {
  @return if($v1 < $v2, $v1, $v2);
}

@function ch-gcd($a, $b) {
  // From: http://rosettacode.org/wiki/Greatest_common_divisor#JavaScript
  @if ($b != 0) {
    @return ch-gcd($b, $a % $b);
  } @else {
    @return abs($a);
  }
}

@function ch-pow($base, $exponent, $prec: 12) {
  // Handles decimal exponents by trying to convert them into a fraction and then use a nthRoot-algorithm for parts of the calculation
  @if (floor($exponent) != $exponent) {
    $prec2: ch-pow(10, $prec);
    $exponent: round($exponent * $prec2);
    $denominator: ch-gcd($exponent, $prec2);
    @return ch-nth-root(ch-pow($base, $exponent / $denominator), $prec2 / $denominator, $prec);
  }

  $value: $base;
  @if $exponent > 1 {
    @for $i from 2 through $exponent {
      $value: $value * $base;
    }
  } @else if $exponent < 1 {
    @for $i from 0 through -$exponent {
      $value: $value / $base;
    }
  }

  @return $value;
}

@function ch-nth-root($num, $n: 2, $prec: 12) {
  // From: http://rosettacode.org/wiki/Nth_root#JavaScript
  $x: 1;

  @for $i from 0 through $prec {
    $x: 1 / $n * (($n - 1) * $x + ($num / ch-pow($x, $n - 1)));
  }

  @return $x;
}

@function ch-hsv-to-hsl($h, $s: 0, $v: 0) {
  @if type_of($h) == 'list' {
    $v: nth($h, 3);
    $s: nth($h, 2);
    $h: nth($h, 1);
  }

  @if unit($h) == 'deg' {
    $h: 3.1415 * 2 * ($h / 360deg);
  }
  @if unit($s) == '%' {
    $s: 0 + (round($s) / 100%);
  } @else {
    $s: $s / 100;
  }
  @if unit($v) == '%' {
    $v: if($v >= 100, 1, $v / 100%);
  }

  $ss: $s * $v;
  $ll: (2 - $s) * $v;

  @if ($ll == 2 or $ll == 0) {
    $ss: 0;
  } @else if $ll <= 1 {
    $ss: $ss / $ll;
  } @else {
    $ss: $ss / (2 - $ll);
  }

  $ll: $ll / 2;

  @return 360deg * $h / (3.1415 * 2), percentage(max(0, min(1, $ss))), percentage(max(0, min(1, $ll)));
}

@function ch-hsl-to-hsv($h, $ss: 0, $ll: 0) {
  @if type_of($h) == 'list' {
    $ll: nth($h, 3);
    $ss: nth($h, 2);
    $h: nth($h, 1);
  } @else if type_of($h) == 'color' {
    $ll: lightness($h);
    $ss: saturation($h);
    $h: hue($h);
  }

  @if unit($h) == 'deg' {
    $h: 3.1415 * 2 * ($h / 360deg);
  }
  @if unit($ss) == '%' {
    $ss: 0 + ($ss / 100%);
  }
  @if unit($ll) == '%' {
    $ll: 0 + ($ll / 100%);
  }

  $ll: $ll * 2;

  @if $ll <= 1 {
    $ss: $ss * $ll;
  } @else {
    $ss: $ss * (2 - $ll);
  }

  $v: ($ll + $ss) / 2;

  $s: 0;
  @if $ll + $ss != 0 {
    $s: (2 * $ss) / ($ll + $ss);
  }

  @return 360deg * $h / (3.1415 * 2), percentage(max(0, min(1, $s))), percentage(max(0, min(1, $v)));
}

@function ch-color-to-hsv($color) {
  @return ch-hsl-to-hsv($color);
}

@function ch-hsv-to-color($h, $s: 0, $v: 0) {
  $hsl: ch-hsv-to-hsl($h, $s, $v);
  //@debug hsla(nth($hsl, 1), nth($hsl, 2), nth($hsl, 3), 1);
  @return hsla(nth($hsl, 1), nth($hsl, 2), nth($hsl, 3), 1);
}

@function ch-brightness($h, $s: 0, $v: 0, $adjustment: 0) {
  @if type_of($h) == 'color' {
    $h: ch-color-to-hsv($h);
  }
  @if type_of($h) == 'list' {
    $adjustment: $s;
    $v: nth($h, 3);
    $s: nth($h, 2);
    $h: nth($h, 1);
  }

  $v: ch-max(0%, ch-min(100%, $v + $adjustment));

  @return ch-hsv-to-color($h, $s, $v);
}
