/***********************************************************************
 * ratpoints-2.1.2                                                     *
 *  - A program to find rational points on hyperelliptic curves        *
 * Copyright (C) 2008, 2009  Michael Stoll                             *
 *                                                                     *
 * 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 version 2 of the GNU General     *
 * Public License along with this program.                             *
 * If not, see <http://www.gnu.org/licenses/>.                         *
 ***********************************************************************/

/***********************************************************************
 * sift.c                                                              *
 *                                                                     *
 * The sieving procedure for ratpoints                                 *
 *                                                                     *
 * Michael Stoll, Apr 14, 2009                                         *
 ***********************************************************************/

#include "rp-private.h"

/**************************************************************************
 * check if m and n are relatively prime                                  *
 **************************************************************************/

static inline int relprime(long m, long n)
{
  /* n (the denominator) is always positive here */
  if(m == 0) { return(n == 1); }
  if(m < 0) m = -m;
  if(!(m & 1))
  { if(!(n & 1)) { return(0); }
    m >>= 1; while(!(m & 1)) { m >>= 1; }
  }
  while(!(n & 1)) { n >>= 1; }
  while(n != m)
  { if(n > m)
    { n -= m; n >>= 1; while(!(n & 1)) { n >>= 1; } }
    else
    { m -= n; m >>= 1; while(!(m & 1)) { m >>= 1; } }
  }
  return(m == 1);
}

/**************************************************************************
 * Try to avoid divisions                                                 *
 **************************************************************************/

static inline long mod(long a, long b)
{
  long b1 = b << 4; /* b1 = 16*b */

  if(a < -b1) { a %= b; if(a < 0) { a += b; } return(a); }
  if(a < 0) { a += b1; }
  else { if(a >= b1) { return(a % b); } }
  b1 >>= 1; /* b1 = 8*b */
  if(a >= b1) { a -= b1; }
  b1 >>= 1; /* b1 = 4*b */
  if(a >= b1) { a -= b1; }
  b1 >>= 1; /* b1 = 2*b */
  if(a >= b1) { a -= b1; }
  if(a >= b) { a -= b; }
  return(a);
}

/**************************************************************************
 * The inner loop of the sieving procedure                                *
 **************************************************************************/

long _ratpoints_sift0(long b, long w_low, long w_high, 
           ratpoints_args *args, bit_selection which_bits,
           ratpoints_bit_array *survivors, sieve_spec *sieves, int *quit,
           int process(long, long, const mpz_t, void*, int*), void *info)
{
  long total = 0; 
  long range = w_high - w_low;
  long sp1 = args->sp1;
  long sp2 = args->sp2;

#ifdef DEBUG
  { long n, c = 0;
    printf("\nsift0(b = %ld) @ start [high numerators to the left]:\n", b);
    for(n = range - 1; n >= 0; n--, c++)
    { if((c & (0xff >> RBA_SHIFT)) == 0) { printf("\n"); }
#ifdef USE_SSE
      printf("%*.*lx%*.*lx ", WIDTH, WIDTH, EXT1(survivors[n]), 
                              WIDTH, WIDTH, EXT0(survivors[n]));
#else
      printf("%*.*lx ", WIDTH, WIDTH, survivors[n]); 
#endif
    }
    printf("\n");
    fflush(NULL);
  }
#endif

  /* now do the sieving (fast!) */

#ifdef DEBUG
  printf("\nsift0: sp1 = %ld, sp2 = %ld\n\n", sp1, sp2);
  fflush(NULL);
#endif

  { long n;
    for(n = 0; n < sp1; n++)
    { ratpoints_bit_array *sieve_n = sieves[n].ptr;
      register long p = sieves[n].p;
      long r = mod(-w_low-sieves[n].offset, p);
      register ratpoints_bit_array *surv = survivors;

      if(w_high < w_low + r)
      { /* if we get here, r > 0, since w_high >= w_low always */
        register ratpoints_bit_array *siv1 = &sieve_n[p-r];
        register ratpoints_bit_array *siv0 = siv1 + range;

        while(siv1 != siv0)
#ifdef USE_SSE
        { *surv = AND(*surv, *siv1++); surv++; }
#else
        { *surv++ &= *siv1++; }
#endif
      }
      else
      { register ratpoints_bit_array *siv1 = &sieve_n[p-r];
        register ratpoints_bit_array *surv_end = &survivors[range - p];

        { register long i;

          for(i = r; i; i--)
#ifdef USE_SSE
          { *surv = AND(*surv, *siv1++); surv++; }
#else
          { *surv++ &= *siv1++; }
#endif
        }
        siv1 -= p;
        while(surv <= surv_end)
        { register long i;

          for(i = p; i; i--)
#ifdef USE_SSE
          { *surv = AND(*surv, *siv1++); surv++; }
#else
          { *surv++ &= *siv1++; }
#endif
          siv1 -= p;
        }
        surv_end += p;
        while(surv < surv_end)
#ifdef USE_SSE
        { *surv = AND(*surv, *siv1++); surv++; }
#else
        { *surv++ &= *siv1++; }
#endif
      }

#ifdef DEBUG
      { long k, c = 0;

        printf("\nsift0 after prime p = %ld [high numerators to the left]:", p);
        for(k = range - 1; k >= 0; k--, c++)
        { if((c & (0xff >> RBA_SHIFT)) == 0) { printf("\n"); }
#ifdef USE_SSE
          printf("%*.*lx%*.*lx ", WIDTH, WIDTH, EXT1(survivors[k]),
                                  WIDTH, WIDTH, EXT0(survivors[k]));
#else
          printf("%*.*lx ", WIDTH, WIDTH, survivors[k]);
#endif
        }
        printf("\n");
        fflush(NULL);
      }
#endif

    } /* for n */
  }

#ifdef DEBUG
  { long n, c = 0;

    printf("\nsift0(b = %ld) after phase 1 [high numerators to the left]:\n", b);
    for(n = range - 1; n >= 0; n--, c++)
    { if((c & (0xff >> RBA_SHIFT)) == 0) { printf("\n"); }
#ifdef USE_SSE
      printf("%*.*lx%*.*lx ", WIDTH, WIDTH, EXT1(survivors[n]),
                              WIDTH, WIDTH, EXT0(survivors[n]));
#else
      printf("%*.*lx ", WIDTH, WIDTH, survivors[n]);
#endif
    }
    printf("\n\n");
    fflush(NULL);
  }
#endif

  /* Second phase of the sieve: test each surviving bit array with more primes */
  { ratpoints_bit_array *surv0 = &survivors[0];
    long i;

    for(i = w_low; i < w_high; i++)
    { register ratpoints_bit_array nums = *surv0++;
      sieve_spec *ssp = &sieves[sp1];
      register long n;

#ifdef DEBUG
#ifdef USE_SSE
      if(TEST(nums))
      { 
        printf("\nsurviving word %*.*lx%*.*lx @ i = %ld\n",
               WIDTH, WIDTH, EXT1(nums), WIDTH, WIDTH, EXT0(nums), i);
#else
      if(nums)
      { 
        printf("\nsurviving word %*.*lx @ i = %ld\n", WIDTH, WIDTH, nums, i);
#endif
        fflush(NULL);
      }
#endif
#ifdef USE_SSE
      for(n = sp2-sp1; n && TEST(nums); n--)
#else
      for(n = sp2-sp1; n && nums; n--)
#endif
      { register long p = ssp->p;

#ifdef USE_SSE
        nums = AND(nums, ssp->ptr[mod(i + ssp->offset, p)]);
#else
        nums &= ssp->ptr[mod(i + ssp->offset, p)];
#endif
#ifdef DEBUG
#ifdef USE_SSE
        printf("after prime p = %ld: %*.*lx%*.*lx\n", p,
               WIDTH, WIDTH, EXT1(nums), WIDTH, WIDTH, EXT0(nums));
#else
        printf("after prime p = %ld: %*.*lx\n", p, WIDTH, WIDTH, nums);
#endif
        fflush(NULL);
#endif
        ssp++;
      }

      /* Check the survivors of the sieve if they really give points */
#ifdef USE_SSE
      if(TEST(nums))
#else
      if(nums)
#endif
      { long a0, a, da, d;
             /* a will be the numerator corresponding to the selected bit */
#ifdef DEBUG
        long bit = 0;
#endif
        if(which_bits == num_all)
        { d = 1; a0 = i << RBA_SHIFT; da = RBA_LENGTH/2; }
        else
        { d = 2; a0 = i << (RBA_SHIFT+1); da = RBA_LENGTH;
          if(which_bits == num_odd) { a0++; }
        }

        {
#ifdef USE_SSE
          unsigned long nums0 = EXT0(nums);
          unsigned long nums1 = EXT1(nums);
#else
          unsigned long nums0 = nums;
#endif

          for(a = a0; nums0; a += d, nums0 >>= 1)
          { /* test one bit */
            if((nums0 & 1) && relprime(a, b))
            {
#ifdef DEBUG
              printf("\nsurviving bit no. %ld --> a = %ld. Check point...\n",
                     bit, a);
              fflush(NULL);
#endif
              total += _ratpoints_check_point(a, b, args, quit, process, info);
              if(*quit) return(total);
            }
#ifdef DEBUG
            bit++;
#endif
          }
#ifdef USE_SSE
#ifdef DEBUG
          bit = 0;
#endif
          for(a = a0 + da; nums1; a += d, nums1 >>= 1)
          { /* test one bit */
            if((nums1 & 1) && relprime(a, b))
            {
#ifdef DEBUG
              printf("\nsurviving bit no. %ld --> a = %ld. Check point...\n",
                     bit+64, a);
              fflush(NULL);
#endif
              total += _ratpoints_check_point(a, b, args, quit, process, info);
              if(*quit) return(total);
            }
#ifdef DEBUG
            bit++;
#endif
          }
#endif
        }
      }
    }
  }
  return(total);
}
